Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/websdk
diff options
context:
space:
mode:
Diffstat (limited to 'websdk')
-rw-r--r--websdk/__init__.py0
-rw-r--r--websdk/flask/__init__.py41
-rw-r--r--websdk/flask/app.py1518
-rw-r--r--websdk/flask/blueprints.py321
-rw-r--r--websdk/flask/config.py169
-rw-r--r--websdk/flask/ctx.py175
-rw-r--r--websdk/flask/debughelpers.py79
-rw-r--r--websdk/flask/ext/__init__.py29
-rw-r--r--websdk/flask/exthook.py119
-rw-r--r--websdk/flask/globals.py28
-rw-r--r--websdk/flask/helpers.py649
-rw-r--r--websdk/flask/logging.py43
-rw-r--r--websdk/flask/module.py42
-rw-r--r--websdk/flask/session.py19
-rw-r--r--websdk/flask/sessions.py205
-rw-r--r--websdk/flask/signals.py51
-rw-r--r--websdk/flask/templating.py138
-rw-r--r--websdk/flask/testing.py118
-rw-r--r--websdk/flask/testsuite/__init__.py221
-rw-r--r--websdk/flask/testsuite/basic.py1051
-rw-r--r--websdk/flask/testsuite/blueprints.py512
-rw-r--r--websdk/flask/testsuite/config.py182
-rw-r--r--websdk/flask/testsuite/deprecations.py41
-rw-r--r--websdk/flask/testsuite/examples.py38
-rw-r--r--websdk/flask/testsuite/ext.py123
-rw-r--r--websdk/flask/testsuite/helpers.py298
-rw-r--r--websdk/flask/testsuite/signals.py103
-rw-r--r--websdk/flask/testsuite/static/index.html1
-rw-r--r--websdk/flask/testsuite/subclassing.py46
-rw-r--r--websdk/flask/testsuite/templates/_macro.html1
-rw-r--r--websdk/flask/testsuite/templates/context_template.html1
-rw-r--r--websdk/flask/testsuite/templates/escaping_template.html6
-rw-r--r--websdk/flask/testsuite/templates/mail.txt1
-rw-r--r--websdk/flask/testsuite/templates/nested/nested.txt1
-rw-r--r--websdk/flask/testsuite/templates/simple_template.html1
-rw-r--r--websdk/flask/testsuite/templates/template_filter.html1
-rw-r--r--websdk/flask/testsuite/templating.py144
-rw-r--r--websdk/flask/testsuite/test_apps/blueprintapp/__init__.py7
-rw-r--r--websdk/flask/testsuite/test_apps/blueprintapp/apps/__init__.py0
-rw-r--r--websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/__init__.py15
-rw-r--r--websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/static/css/test.css1
-rw-r--r--websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/static/test.txt1
-rw-r--r--websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/templates/admin/index.html1
-rw-r--r--websdk/flask/testsuite/test_apps/blueprintapp/apps/frontend/__init__.py8
-rw-r--r--websdk/flask/testsuite/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html1
-rw-r--r--websdk/flask/testsuite/test_apps/config_module_app.py4
-rw-r--r--websdk/flask/testsuite/test_apps/config_package_app/__init__.py4
-rw-r--r--websdk/flask/testsuite/test_apps/flask_broken/__init__.py2
-rw-r--r--websdk/flask/testsuite/test_apps/flask_broken/b.py0
-rw-r--r--websdk/flask/testsuite/test_apps/flask_newext_package/__init__.py1
-rw-r--r--websdk/flask/testsuite/test_apps/flask_newext_package/submodule.py2
-rw-r--r--websdk/flask/testsuite/test_apps/flask_newext_simple.py1
-rw-r--r--websdk/flask/testsuite/test_apps/flaskext/__init__.py0
-rw-r--r--websdk/flask/testsuite/test_apps/flaskext/oldext_package/__init__.py1
-rw-r--r--websdk/flask/testsuite/test_apps/flaskext/oldext_package/submodule.py2
-rw-r--r--websdk/flask/testsuite/test_apps/flaskext/oldext_simple.py1
-rw-r--r--websdk/flask/testsuite/test_apps/moduleapp/__init__.py7
-rw-r--r--websdk/flask/testsuite/test_apps/moduleapp/apps/__init__.py0
-rw-r--r--websdk/flask/testsuite/test_apps/moduleapp/apps/admin/__init__.py14
-rw-r--r--websdk/flask/testsuite/test_apps/moduleapp/apps/admin/static/css/test.css1
-rw-r--r--websdk/flask/testsuite/test_apps/moduleapp/apps/admin/static/test.txt1
-rw-r--r--websdk/flask/testsuite/test_apps/moduleapp/apps/admin/templates/index.html1
-rw-r--r--websdk/flask/testsuite/test_apps/moduleapp/apps/frontend/__init__.py9
-rw-r--r--websdk/flask/testsuite/test_apps/moduleapp/apps/frontend/templates/index.html1
-rw-r--r--websdk/flask/testsuite/test_apps/subdomaintestmodule/__init__.py4
-rw-r--r--websdk/flask/testsuite/test_apps/subdomaintestmodule/static/hello.txt1
-rw-r--r--websdk/flask/testsuite/testing.py171
-rw-r--r--websdk/flask/testsuite/views.py152
-rw-r--r--websdk/flask/views.py151
-rw-r--r--websdk/flask/wrappers.py138
-rw-r--r--websdk/flaskext/__init__.py1
-rw-r--r--websdk/flaskext/genshi.py294
-rw-r--r--websdk/genshi/__init__.py26
-rw-r--r--websdk/genshi/builder.py359
-rw-r--r--websdk/genshi/core.py727
-rw-r--r--websdk/genshi/filters/__init__.py20
-rw-r--r--websdk/genshi/filters/html.py453
-rw-r--r--websdk/genshi/filters/i18n.py1238
-rw-r--r--websdk/genshi/filters/transform.py1310
-rw-r--r--websdk/genshi/input.py443
-rw-r--r--websdk/genshi/output.py838
-rw-r--r--websdk/genshi/path.py1528
-rw-r--r--websdk/genshi/template/__init__.py23
-rw-r--r--websdk/genshi/template/_ast24.py446
-rw-r--r--websdk/genshi/template/ast24.py505
-rw-r--r--websdk/genshi/template/astutil.py784
-rw-r--r--websdk/genshi/template/base.py634
-rw-r--r--websdk/genshi/template/directives.py725
-rw-r--r--websdk/genshi/template/eval.py629
-rw-r--r--websdk/genshi/template/interpolation.py153
-rw-r--r--websdk/genshi/template/loader.py344
-rw-r--r--websdk/genshi/template/markup.py397
-rw-r--r--websdk/genshi/template/plugin.py176
-rw-r--r--websdk/genshi/template/text.py333
-rw-r--r--websdk/genshi/util.py274
-rw-r--r--websdk/jinja2/__init__.py69
-rw-r--r--websdk/jinja2/_debugsupport.c78
-rw-r--r--websdk/jinja2/_markupsafe/__init__.py225
-rw-r--r--websdk/jinja2/_markupsafe/_bundle.py49
-rw-r--r--websdk/jinja2/_markupsafe/_constants.py267
-rw-r--r--websdk/jinja2/_markupsafe/_native.py45
-rw-r--r--websdk/jinja2/_markupsafe/tests.py80
-rw-r--r--websdk/jinja2/_stringdefs.py130
-rw-r--r--websdk/jinja2/bccache.py301
-rw-r--r--websdk/jinja2/compiler.py1649
-rw-r--r--websdk/jinja2/constants.py32
-rw-r--r--websdk/jinja2/debug.py339
-rw-r--r--websdk/jinja2/defaults.py40
-rw-r--r--websdk/jinja2/environment.py1121
-rw-r--r--websdk/jinja2/exceptions.py143
-rw-r--r--websdk/jinja2/ext.py612
-rw-r--r--websdk/jinja2/filters.py801
-rw-r--r--websdk/jinja2/lexer.py681
-rw-r--r--websdk/jinja2/loaders.py473
-rw-r--r--websdk/jinja2/meta.py102
-rw-r--r--websdk/jinja2/nodes.py910
-rw-r--r--websdk/jinja2/optimizer.py68
-rw-r--r--websdk/jinja2/parser.py895
-rw-r--r--websdk/jinja2/runtime.py548
-rw-r--r--websdk/jinja2/sandbox.py361
-rw-r--r--websdk/jinja2/tests.py161
-rw-r--r--websdk/jinja2/testsuite/__init__.py95
-rw-r--r--websdk/jinja2/testsuite/api.py245
-rw-r--r--websdk/jinja2/testsuite/core_tags.py285
-rw-r--r--websdk/jinja2/testsuite/debug.py60
-rw-r--r--websdk/jinja2/testsuite/doctests.py29
-rw-r--r--websdk/jinja2/testsuite/ext.py455
-rw-r--r--websdk/jinja2/testsuite/filters.py356
-rw-r--r--websdk/jinja2/testsuite/imports.py141
-rw-r--r--websdk/jinja2/testsuite/inheritance.py227
-rw-r--r--websdk/jinja2/testsuite/lexnparse.py387
-rw-r--r--websdk/jinja2/testsuite/loader.py218
-rw-r--r--websdk/jinja2/testsuite/regression.py255
-rw-r--r--websdk/jinja2/testsuite/res/__init__.py0
-rw-r--r--websdk/jinja2/testsuite/res/templates/broken.html3
-rw-r--r--websdk/jinja2/testsuite/res/templates/foo/test.html1
-rw-r--r--websdk/jinja2/testsuite/res/templates/syntaxerror.html4
-rw-r--r--websdk/jinja2/testsuite/res/templates/test.html1
-rw-r--r--websdk/jinja2/testsuite/security.py165
-rw-r--r--websdk/jinja2/testsuite/tests.py93
-rw-r--r--websdk/jinja2/testsuite/utils.py82
-rw-r--r--websdk/jinja2/utils.py601
-rw-r--r--websdk/jinja2/visitor.py87
-rw-r--r--websdk/static/css/main.css118
-rw-r--r--websdk/static/css/sugar-theme/images/ui-bg_flat_100_c0c0c0_40x100.pngbin0 -> 211 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-bg_flat_50_aaaaaa_40x100.pngbin0 -> 180 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-bg_flat_65_ffffff_40x100.pngbin0 -> 178 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-bg_flat_75_282828_40x100.pngbin0 -> 180 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-bg_flat_75_808080_40x100.pngbin0 -> 209 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-bg_glow-ball_20_282828_600x600.pngbin0 -> 22287 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-bg_highlight-hard_5_282828_1x100.pngbin0 -> 97 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-bg_highlight-hard_95_c0c0c0_1x100.pngbin0 -> 148 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-icons_000000_256x240.pngbin0 -> 4369 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-icons_2e83ff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-icons_cd0a0a_256x240.pngbin0 -> 4369 bytes
-rw-r--r--websdk/static/css/sugar-theme/images/ui-icons_ffffff_256x240.pngbin0 -> 4369 bytes
-rw-r--r--websdk/static/css/sugar-theme/jquery-ui-1.8.15.custom.css568
l---------websdk/static/icons1
-rw-r--r--websdk/static/images/throbber-120.gifbin0 -> 10701 bytes
-rw-r--r--websdk/static/init.html26
-rw-r--r--websdk/static/js/ace/ace-uncompressed.js15284
-rw-r--r--websdk/static/js/ace/ace.js1
-rw-r--r--websdk/static/js/ace/cockpit-uncompressed.js2504
-rw-r--r--websdk/static/js/ace/cockpit.js1
-rw-r--r--websdk/static/js/ace/keybinding-emacs.js1
-rw-r--r--websdk/static/js/ace/keybinding-vim.js1
-rw-r--r--websdk/static/js/ace/mode-c_cpp.js1
-rw-r--r--websdk/static/js/ace/mode-clojure.js1
-rw-r--r--websdk/static/js/ace/mode-coffee.js1
-rw-r--r--websdk/static/js/ace/mode-csharp.js1
-rw-r--r--websdk/static/js/ace/mode-css.js1
-rw-r--r--websdk/static/js/ace/mode-html.js1
-rw-r--r--websdk/static/js/ace/mode-java.js1
-rw-r--r--websdk/static/js/ace/mode-javascript.js1
-rw-r--r--websdk/static/js/ace/mode-json.js1
-rw-r--r--websdk/static/js/ace/mode-perl.js1
-rw-r--r--websdk/static/js/ace/mode-php.js1
-rw-r--r--websdk/static/js/ace/mode-python.js1
-rw-r--r--websdk/static/js/ace/mode-ruby.js1
-rw-r--r--websdk/static/js/ace/mode-scss.js1
-rw-r--r--websdk/static/js/ace/mode-svg.js1
-rw-r--r--websdk/static/js/ace/mode-xml.js1
-rw-r--r--websdk/static/js/ace/theme-clouds.js1
-rw-r--r--websdk/static/js/ace/theme-clouds_midnight.js1
-rw-r--r--websdk/static/js/ace/theme-cobalt.js1
-rw-r--r--websdk/static/js/ace/theme-dawn.js1
-rw-r--r--websdk/static/js/ace/theme-eclipse.js1
-rw-r--r--websdk/static/js/ace/theme-idle_fingers.js1
-rw-r--r--websdk/static/js/ace/theme-kr_theme.js1
-rw-r--r--websdk/static/js/ace/theme-merbivore.js1
-rw-r--r--websdk/static/js/ace/theme-merbivore_soft.js1
-rw-r--r--websdk/static/js/ace/theme-mono_industrial.js1
-rw-r--r--websdk/static/js/ace/theme-monokai.js1
-rw-r--r--websdk/static/js/ace/theme-pastel_on_dark.js1
-rw-r--r--websdk/static/js/ace/theme-twilight.js1
-rw-r--r--websdk/static/js/ace/theme-vibrant_ink.js1
-rw-r--r--websdk/static/js/ace/worker-coffee.js1
-rw-r--r--websdk/static/js/ace/worker-css.js1
-rw-r--r--websdk/static/js/ace/worker-javascript.js1
-rw-r--r--websdk/static/js/jquery-1.6.2.js8981
-rw-r--r--websdk/static/js/jquery-ui-1.8.15.custom.min.js790
-rw-r--r--websdk/static/js/jquery.corner.js249
-rw-r--r--websdk/static/js/jquery.plugin.svgimg.js33
-rw-r--r--websdk/static/js/jquery.quicksand.js307
-rw-r--r--websdk/static/js/websdk-1.js6
-rw-r--r--websdk/static/js/wymeditor/iframe/.svn/entries31
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/entries402
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-blockquote.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h1.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h2.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h3.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h4.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h5.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h6.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-p.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-pre.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-blockquote.png.svn-basebin0 -> 196 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h1.png.svn-basebin0 -> 166 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h2.png.svn-basebin0 -> 172 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h3.png.svn-basebin0 -> 170 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h4.png.svn-basebin0 -> 172 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h5.png.svn-basebin0 -> 172 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h6.png.svn-basebin0 -> 171 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-p.png.svn-basebin0 -> 3607 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-pre.png.svn-basebin0 -> 177 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.css.svn-base90
-rw-r--r--websdk/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.html.svn-base26
-rw-r--r--websdk/static/js/wymeditor/iframe/default/lbl-blockquote.pngbin0 -> 196 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/lbl-h1.pngbin0 -> 166 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/lbl-h2.pngbin0 -> 172 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/lbl-h3.pngbin0 -> 170 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/lbl-h4.pngbin0 -> 172 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/lbl-h5.pngbin0 -> 172 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/lbl-h6.pngbin0 -> 171 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/lbl-p.pngbin0 -> 3607 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/lbl-pre.pngbin0 -> 177 bytes
-rw-r--r--websdk/static/js/wymeditor/iframe/default/wymiframe.css90
-rw-r--r--websdk/static/js/wymeditor/iframe/default/wymiframe.html26
-rw-r--r--websdk/static/js/wymeditor/jquery.wymeditor.explorer.js218
-rw-r--r--websdk/static/js/wymeditor/jquery.wymeditor.js3860
-rw-r--r--websdk/static/js/wymeditor/jquery.wymeditor.min.js1
-rw-r--r--websdk/static/js/wymeditor/jquery.wymeditor.mozilla.js278
-rw-r--r--websdk/static/js/wymeditor/jquery.wymeditor.opera.js119
-rw-r--r--websdk/static/js/wymeditor/jquery.wymeditor.pack.js1
-rw-r--r--websdk/static/js/wymeditor/jquery.wymeditor.safari.js247
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/entries912
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/bg.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/ca.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/cs.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/cy.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/de.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/en.js.svn-base46
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/es.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/fa.js.svn-base46
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/fi.js.svn-base44
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/fr.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/gl.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/he.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/hr.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/hu.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/it.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/ja.js.svn-base44
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/nb.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/nl.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/nn.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/pl.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/pt-br.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/pt.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/ru.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/sv.js.svn-base46
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/tr.js.svn-base45
-rw-r--r--websdk/static/js/wymeditor/lang/.svn/text-base/zh_cn.js.svn-base47
-rw-r--r--websdk/static/js/wymeditor/lang/bg.js45
-rw-r--r--websdk/static/js/wymeditor/lang/ca.js45
-rw-r--r--websdk/static/js/wymeditor/lang/cs.js45
-rw-r--r--websdk/static/js/wymeditor/lang/cy.js45
-rw-r--r--websdk/static/js/wymeditor/lang/de.js45
-rw-r--r--websdk/static/js/wymeditor/lang/en.js46
-rw-r--r--websdk/static/js/wymeditor/lang/es.js45
-rw-r--r--websdk/static/js/wymeditor/lang/fa.js46
-rw-r--r--websdk/static/js/wymeditor/lang/fi.js44
-rw-r--r--websdk/static/js/wymeditor/lang/fr.js45
-rw-r--r--websdk/static/js/wymeditor/lang/gl.js45
-rw-r--r--websdk/static/js/wymeditor/lang/he.js45
-rw-r--r--websdk/static/js/wymeditor/lang/hr.js45
-rw-r--r--websdk/static/js/wymeditor/lang/hu.js45
-rw-r--r--websdk/static/js/wymeditor/lang/it.js45
-rw-r--r--websdk/static/js/wymeditor/lang/ja.js44
-rw-r--r--websdk/static/js/wymeditor/lang/nb.js45
-rw-r--r--websdk/static/js/wymeditor/lang/nl.js45
-rw-r--r--websdk/static/js/wymeditor/lang/nn.js45
-rw-r--r--websdk/static/js/wymeditor/lang/pl.js45
-rw-r--r--websdk/static/js/wymeditor/lang/pt-br.js45
-rw-r--r--websdk/static/js/wymeditor/lang/pt.js45
-rw-r--r--websdk/static/js/wymeditor/lang/ru.js45
-rw-r--r--websdk/static/js/wymeditor/lang/sv.js46
-rw-r--r--websdk/static/js/wymeditor/lang/tr.js45
-rw-r--r--websdk/static/js/wymeditor/lang/zh_cn.js47
-rw-r--r--websdk/static/js/wymeditor/plugins/.svn/entries43
-rw-r--r--websdk/static/js/wymeditor/plugins/embed/.svn/entries62
-rw-r--r--websdk/static/js/wymeditor/plugins/embed/.svn/text-base/jquery.wymeditor.embed.js.svn-base52
-rw-r--r--websdk/static/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js52
-rw-r--r--websdk/static/js/wymeditor/plugins/fullscreen/.svn/entries96
-rw-r--r--websdk/static/js/wymeditor/plugins/fullscreen/.svn/prop-base/icon_fullscreen.gif.svn-base5
-rw-r--r--websdk/static/js/wymeditor/plugins/fullscreen/.svn/text-base/icon_fullscreen.gif.svn-basebin0 -> 509 bytes
-rw-r--r--websdk/static/js/wymeditor/plugins/fullscreen/.svn/text-base/jquery.wymeditor.fullscreen.js.svn-base127
-rw-r--r--websdk/static/js/wymeditor/plugins/fullscreen/icon_fullscreen.gifbin0 -> 509 bytes
-rw-r--r--websdk/static/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js127
-rw-r--r--websdk/static/js/wymeditor/plugins/hovertools/.svn/entries62
-rw-r--r--websdk/static/js/wymeditor/plugins/hovertools/.svn/text-base/jquery.wymeditor.hovertools.js.svn-base57
-rw-r--r--websdk/static/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js57
-rw-r--r--websdk/static/js/wymeditor/plugins/resizable/.svn/entries96
-rw-r--r--websdk/static/js/wymeditor/plugins/resizable/.svn/text-base/jquery.wymeditor.resizable.js.svn-base91
-rw-r--r--websdk/static/js/wymeditor/plugins/resizable/.svn/text-base/readme.txt.svn-base124
-rw-r--r--websdk/static/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js91
-rw-r--r--websdk/static/js/wymeditor/plugins/resizable/readme.txt124
-rw-r--r--websdk/static/js/wymeditor/plugins/tidy/.svn/entries164
-rw-r--r--websdk/static/js/wymeditor/plugins/tidy/.svn/prop-base/wand.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/README.svn-base19
-rw-r--r--websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/jquery.wymeditor.tidy.js.svn-base82
-rw-r--r--websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/tidy.php.svn-base36
-rw-r--r--websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/wand.png.svn-basebin0 -> 715 bytes
-rw-r--r--websdk/static/js/wymeditor/plugins/tidy/README19
-rw-r--r--websdk/static/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js82
-rw-r--r--websdk/static/js/wymeditor/plugins/tidy/tidy.php36
-rw-r--r--websdk/static/js/wymeditor/plugins/tidy/wand.pngbin0 -> 715 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/.svn/entries77
-rw-r--r--websdk/static/js/wymeditor/skins/.svn/prop-base/wymeditor_icon.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/.svn/text-base/wymeditor_icon.png.svn-basebin0 -> 1028 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/compact/.svn/entries130
-rw-r--r--websdk/static/js/wymeditor/skins/compact/.svn/prop-base/icons.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/compact/.svn/text-base/icons.png.svn-basebin0 -> 3651 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/compact/.svn/text-base/skin.css.svn-base134
-rw-r--r--websdk/static/js/wymeditor/skins/compact/.svn/text-base/skin.js.svn-base35
-rw-r--r--websdk/static/js/wymeditor/skins/compact/icons.pngbin0 -> 3651 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/compact/skin.css134
-rw-r--r--websdk/static/js/wymeditor/skins/compact/skin.js35
-rw-r--r--websdk/static/js/wymeditor/skins/default/.svn/entries130
-rw-r--r--websdk/static/js/wymeditor/skins/default/.svn/prop-base/icons.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/default/.svn/text-base/icons.png.svn-basebin0 -> 3651 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/default/.svn/text-base/skin.css.svn-base133
-rw-r--r--websdk/static/js/wymeditor/skins/default/.svn/text-base/skin.js.svn-base40
-rw-r--r--websdk/static/js/wymeditor/skins/default/icons.pngbin0 -> 3651 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/default/skin.css133
-rw-r--r--websdk/static/js/wymeditor/skins/default/skin.js40
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/.svn/entries99
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/.svn/text-base/skin.css.svn-base131
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/.svn/text-base/skin.js.svn-base30
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/.svn/entries164
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.header.gif.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.selector.silver.gif.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.wymeditor.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/icons.silver.gif.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.header.gif.svn-basebin0 -> 781 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.selector.silver.gif.svn-basebin0 -> 1621 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.wymeditor.png.svn-basebin0 -> 498 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/icons.silver.gif.svn-basebin0 -> 15382 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/bg.header.gifbin0 -> 781 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/bg.selector.silver.gifbin0 -> 1621 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/bg.wymeditor.pngbin0 -> 498 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/images/icons.silver.gifbin0 -> 15382 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/skin.css131
-rw-r--r--websdk/static/js/wymeditor/skins/minimal/skin.js30
-rw-r--r--websdk/static/js/wymeditor/skins/silver/.svn/entries167
-rw-r--r--websdk/static/js/wymeditor/skins/silver/.svn/prop-base/COPYING.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/silver/.svn/prop-base/README.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/silver/.svn/text-base/COPYING.svn-base674
-rw-r--r--websdk/static/js/wymeditor/skins/silver/.svn/text-base/README.svn-base27
-rw-r--r--websdk/static/js/wymeditor/skins/silver/.svn/text-base/skin.css.svn-base297
-rw-r--r--websdk/static/js/wymeditor/skins/silver/.svn/text-base/skin.js.svn-base61
-rwxr-xr-xwebsdk/static/js/wymeditor/skins/silver/COPYING674
-rwxr-xr-xwebsdk/static/js/wymeditor/skins/silver/README27
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/.svn/entries164
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.header.gif.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.selector.silver.gif.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.wymeditor.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/icons.silver.gif.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.header.gif.svn-basebin0 -> 781 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.selector.silver.gif.svn-basebin0 -> 1621 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.wymeditor.png.svn-basebin0 -> 498 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/icons.silver.gif.svn-basebin0 -> 15382 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/bg.header.gifbin0 -> 781 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/bg.selector.silver.gifbin0 -> 1621 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/bg.wymeditor.pngbin0 -> 498 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/silver/images/icons.silver.gifbin0 -> 15382 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/silver/skin.css297
-rw-r--r--websdk/static/js/wymeditor/skins/silver/skin.js61
-rw-r--r--websdk/static/js/wymeditor/skins/twopanels/.svn/entries130
-rw-r--r--websdk/static/js/wymeditor/skins/twopanels/.svn/prop-base/icons.png.svn-base5
-rw-r--r--websdk/static/js/wymeditor/skins/twopanels/.svn/text-base/icons.png.svn-basebin0 -> 3651 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.css.svn-base134
-rw-r--r--websdk/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.js.svn-base39
-rw-r--r--websdk/static/js/wymeditor/skins/twopanels/icons.pngbin0 -> 3651 bytes
-rw-r--r--websdk/static/js/wymeditor/skins/twopanels/skin.css134
-rw-r--r--websdk/static/js/wymeditor/skins/twopanels/skin.js39
-rw-r--r--websdk/static/js/wymeditor/skins/wymeditor_icon.pngbin0 -> 1028 bytes
l---------websdk/static/static1
-rw-r--r--websdk/studio.py105
-rw-r--r--websdk/templates/editor.html67
-rw-r--r--websdk/templates/filer.html44
-rw-r--r--websdk/templates/index.html52
-rw-r--r--websdk/templates/skel.html19
-rw-r--r--websdk/templates/split-view.html10
-rw-r--r--websdk/templates/wysiwyg-editor.html54
-rw-r--r--websdk/webkit_local/__init__.py5
-rw-r--r--websdk/webkit_local/libWebKitGdk.so.1bin0 -> 11256488 bytes
-rw-r--r--websdk/webkit_local/libxslt.so.1bin0 -> 222060 bytes
-rw-r--r--websdk/webkit_local/webkit.sobin0 -> 34527 bytes
-rw-r--r--websdk/webpy.py174
-rw-r--r--websdk/werkzeug/__init__.py149
-rw-r--r--websdk/werkzeug/_internal.py405
-rw-r--r--websdk/werkzeug/contrib/__init__.py16
-rw-r--r--websdk/werkzeug/contrib/atom.py343
-rw-r--r--websdk/werkzeug/contrib/cache.py635
-rw-r--r--websdk/werkzeug/contrib/fixers.py224
-rw-r--r--websdk/werkzeug/contrib/iterio.py277
-rw-r--r--websdk/werkzeug/contrib/jsrouting.py259
-rw-r--r--websdk/werkzeug/contrib/kickstart.py288
-rw-r--r--websdk/werkzeug/contrib/limiter.py36
-rw-r--r--websdk/werkzeug/contrib/lint.py333
-rw-r--r--websdk/werkzeug/contrib/profiler.py118
-rw-r--r--websdk/werkzeug/contrib/securecookie.py332
-rw-r--r--websdk/werkzeug/contrib/sessions.py344
-rw-r--r--websdk/werkzeug/contrib/testtools.py71
-rw-r--r--websdk/werkzeug/contrib/wrappers.py275
-rw-r--r--websdk/werkzeug/datastructures.py2567
-rw-r--r--websdk/werkzeug/debug/__init__.py185
-rw-r--r--websdk/werkzeug/debug/console.py204
-rw-r--r--websdk/werkzeug/debug/repr.py267
-rw-r--r--websdk/werkzeug/debug/shared/FONT_LICENSE96
-rwxr-xr-xwebsdk/werkzeug/debug/shared/console.pngbin0 -> 507 bytes
-rw-r--r--websdk/werkzeug/debug/shared/debugger.js200
-rw-r--r--websdk/werkzeug/debug/shared/jquery.js167
-rwxr-xr-xwebsdk/werkzeug/debug/shared/less.pngbin0 -> 191 bytes
-rwxr-xr-xwebsdk/werkzeug/debug/shared/more.pngbin0 -> 200 bytes
-rwxr-xr-xwebsdk/werkzeug/debug/shared/source.pngbin0 -> 818 bytes
-rw-r--r--websdk/werkzeug/debug/shared/style.css113
-rw-r--r--websdk/werkzeug/debug/shared/ubuntu.ttfbin0 -> 70220 bytes
-rw-r--r--websdk/werkzeug/debug/tbtools.py483
-rw-r--r--websdk/werkzeug/exceptions.py536
-rw-r--r--websdk/werkzeug/formparser.py463
-rw-r--r--websdk/werkzeug/http.py841
-rw-r--r--websdk/werkzeug/local.py394
-rw-r--r--websdk/werkzeug/posixemulation.py105
-rw-r--r--websdk/werkzeug/routing.py1620
-rw-r--r--websdk/werkzeug/script.py313
-rw-r--r--websdk/werkzeug/security.py140
-rw-r--r--websdk/werkzeug/serving.py614
-rw-r--r--websdk/werkzeug/templates.py399
-rw-r--r--websdk/werkzeug/test.py843
-rw-r--r--websdk/werkzeug/testapp.py230
-rw-r--r--websdk/werkzeug/testsuite/__init__.py146
-rw-r--r--websdk/werkzeug/testsuite/compat.py58
-rw-r--r--websdk/werkzeug/testsuite/contrib/__init__.py19
-rw-r--r--websdk/werkzeug/testsuite/contrib/cache.py152
-rw-r--r--websdk/werkzeug/testsuite/contrib/fixers.py187
-rw-r--r--websdk/werkzeug/testsuite/contrib/iterio.py72
-rw-r--r--websdk/werkzeug/testsuite/contrib/securecookie.py65
-rw-r--r--websdk/werkzeug/testsuite/contrib/sessions.py81
-rw-r--r--websdk/werkzeug/testsuite/contrib/wrappers.py97
-rw-r--r--websdk/werkzeug/testsuite/datastructures.py618
-rw-r--r--websdk/werkzeug/testsuite/debug.py163
-rw-r--r--websdk/werkzeug/testsuite/exceptions.py87
-rw-r--r--websdk/werkzeug/testsuite/formparser.py370
-rw-r--r--websdk/werkzeug/testsuite/http.py392
-rw-r--r--websdk/werkzeug/testsuite/internal.py81
-rw-r--r--websdk/werkzeug/testsuite/local.py133
-rw-r--r--websdk/werkzeug/testsuite/multipart/collect.py56
-rw-r--r--websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/file1.pngbin0 -> 523 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/file2.pngbin0 -> 703 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/request.txtbin0 -> 1739 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/text.txt1
-rw-r--r--websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/file1.pngbin0 -> 781 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/file2.pngbin0 -> 733 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/request.txtbin0 -> 2042 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/text.txt3
-rw-r--r--websdk/werkzeug/testsuite/multipart/ie6-2png1txt/file1.pngbin0 -> 523 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/ie6-2png1txt/file2.pngbin0 -> 703 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/ie6-2png1txt/request.txtbin0 -> 1798 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/ie6-2png1txt/text.txt1
-rw-r--r--websdk/werkzeug/testsuite/multipart/ie7_full_path_request.txtbin0 -> 30044 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/opera8-2png1txt/file1.pngbin0 -> 582 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/opera8-2png1txt/file2.pngbin0 -> 733 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/opera8-2png1txt/request.txtbin0 -> 1740 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/opera8-2png1txt/text.txt1
-rw-r--r--websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/file1.pngbin0 -> 1002 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/file2.pngbin0 -> 952 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/request.txtbin0 -> 2408 bytes
-rw-r--r--websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/text.txt1
-rw-r--r--websdk/werkzeug/testsuite/res/test.txt1
-rw-r--r--websdk/werkzeug/testsuite/routing.py610
-rw-r--r--websdk/werkzeug/testsuite/security.py57
-rw-r--r--websdk/werkzeug/testsuite/serving.py84
-rw-r--r--websdk/werkzeug/testsuite/test.py376
-rw-r--r--websdk/werkzeug/testsuite/urls.py168
-rw-r--r--websdk/werkzeug/testsuite/utils.py255
-rw-r--r--websdk/werkzeug/testsuite/wrappers.py662
-rw-r--r--websdk/werkzeug/testsuite/wsgi.py217
-rw-r--r--websdk/werkzeug/urls.py539
-rw-r--r--websdk/werkzeug/useragents.py185
-rw-r--r--websdk/werkzeug/utils.py613
-rw-r--r--websdk/werkzeug/wrappers.py1641
-rw-r--r--websdk/werkzeug/wsgi.py830
503 files changed, 102193 insertions, 0 deletions
diff --git a/websdk/__init__.py b/websdk/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/websdk/__init__.py
diff --git a/websdk/flask/__init__.py b/websdk/flask/__init__.py
new file mode 100644
index 0000000..c1076c3
--- /dev/null
+++ b/websdk/flask/__init__.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+"""
+ flask
+ ~~~~~
+
+ A microframework based on Werkzeug. It's extensively documented
+ and follows best practice patterns.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+__version__ = '0.8-dev'
+
+# utilities we import from Werkzeug and Jinja2 that are unused
+# in the module but are exported as public interface.
+from werkzeug.exceptions import abort
+from werkzeug.utils import redirect
+from jinja2 import Markup, escape
+
+from .app import Flask, Request, Response
+from .config import Config
+from .helpers import url_for, jsonify, json_available, flash, \
+ send_file, send_from_directory, get_flashed_messages, \
+ get_template_attribute, make_response, safe_join
+from .globals import current_app, g, request, session, _request_ctx_stack
+from .ctx import has_request_context
+from .module import Module
+from .blueprints import Blueprint
+from .templating import render_template, render_template_string
+
+# the signals
+from .signals import signals_available, template_rendered, request_started, \
+ request_finished, got_request_exception, request_tearing_down
+
+# only import json if it's available
+if json_available:
+ from .helpers import json
+
+# backwards compat, goes away in 1.0
+from .sessions import SecureCookieSession as Session
diff --git a/websdk/flask/app.py b/websdk/flask/app.py
new file mode 100644
index 0000000..ebf4e6a
--- /dev/null
+++ b/websdk/flask/app.py
@@ -0,0 +1,1518 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.app
+ ~~~~~~~~~
+
+ This module implements the central WSGI application object.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import os
+import sys
+from threading import Lock
+from datetime import timedelta
+from itertools import chain
+from functools import update_wrapper
+
+from werkzeug.datastructures import ImmutableDict
+from werkzeug.routing import Map, Rule, RequestRedirect
+from werkzeug.exceptions import HTTPException, InternalServerError, \
+ MethodNotAllowed, BadRequest
+
+from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \
+ locked_cached_property, _tojson_filter, _endpoint_from_view_func, \
+ find_package
+from .wrappers import Request, Response
+from .config import ConfigAttribute, Config
+from .ctx import RequestContext
+from .globals import _request_ctx_stack, request
+from .sessions import SecureCookieSessionInterface
+from .module import blueprint_is_module
+from .templating import DispatchingJinjaLoader, Environment, \
+ _default_template_ctx_processor
+from .signals import request_started, request_finished, got_request_exception, \
+ request_tearing_down
+
+# a lock used for logger initialization
+_logger_lock = Lock()
+
+
+def _make_timedelta(value):
+ if not isinstance(value, timedelta):
+ return timedelta(seconds=value)
+ return value
+
+
+def setupmethod(f):
+ """Wraps a method so that it performs a check in debug mode if the
+ first request was already handled.
+ """
+ def wrapper_func(self, *args, **kwargs):
+ if self.debug and self._got_first_request:
+ raise AssertionError('A setup function was called after the '
+ 'first request was handled. This usually indicates a bug '
+ 'in the application where a module was not imported '
+ 'and decorators or other functionality was called too late.\n'
+ 'To fix this make sure to import all your view modules, '
+ 'database models and everything related at a central place '
+ 'before the application starts serving requests.')
+ return f(self, *args, **kwargs)
+ return update_wrapper(wrapper_func, f)
+
+
+class Flask(_PackageBoundObject):
+ """The flask object implements a WSGI application and acts as the central
+ object. It is passed the name of the module or package of the
+ application. Once it is created it will act as a central registry for
+ the view functions, the URL rules, template configuration and much more.
+
+ The name of the package is used to resolve resources from inside the
+ package or the folder the module is contained in depending on if the
+ package parameter resolves to an actual python package (a folder with
+ an `__init__.py` file inside) or a standard module (just a `.py` file).
+
+ For more information about resource loading, see :func:`open_resource`.
+
+ Usually you create a :class:`Flask` instance in your main module or
+ in the `__init__.py` file of your package like this::
+
+ from flask import Flask
+ app = Flask(__name__)
+
+ .. admonition:: About the First Parameter
+
+ The idea of the first parameter is to give Flask an idea what
+ belongs to your application. This name is used to find resources
+ on the file system, can be used by extensions to improve debugging
+ information and a lot more.
+
+ So it's important what you provide there. If you are using a single
+ module, `__name__` is always the correct value. If you however are
+ using a package, it's usually recommended to hardcode the name of
+ your package there.
+
+ For example if your application is defined in `yourapplication/app.py`
+ you should create it with one of the two versions below::
+
+ app = Flask('yourapplication')
+ app = Flask(__name__.split('.')[0])
+
+ Why is that? The application will work even with `__name__`, thanks
+ to how resources are looked up. However it will make debugging more
+ painful. Certain extensions can make assumptions based on the
+ import name of your application. For example the Flask-SQLAlchemy
+ extension will look for the code in your application that triggered
+ an SQL query in debug mode. If the import name is not properly set
+ up, that debugging information is lost. (For example it would only
+ pick up SQL queries in `yourapplication.app` and not
+ `yourapplication.views.frontend`)
+
+ .. versionadded:: 0.7
+ The `static_url_path`, `static_folder`, and `template_folder`
+ parameters were added.
+
+ .. versionadded:: 0.8
+ The `instance_path` and `instance_relative_config` parameters were
+ added.
+
+ :param import_name: the name of the application package
+ :param static_url_path: can be used to specify a different path for the
+ static files on the web. Defaults to the name
+ of the `static_folder` folder.
+ :param static_folder: the folder with static files that should be served
+ at `static_url_path`. Defaults to the ``'static'``
+ folder in the root path of the application.
+ :param template_folder: the folder that contains the templates that should
+ be used by the application. Defaults to
+ ``'templates'`` folder in the root path of the
+ application.
+ :param instance_path: An alternative instance path for the application.
+ By default the folder ``'instance'`` next to the
+ package or module is assumed to be the instance
+ path.
+ :param instance_relative_config: if set to `True` relative filenames
+ for loading the config are assumed to
+ be relative to the instance path instead
+ of the application root.
+ """
+
+ #: The class that is used for request objects. See :class:`~flask.Request`
+ #: for more information.
+ request_class = Request
+
+ #: The class that is used for response objects. See
+ #: :class:`~flask.Response` for more information.
+ response_class = Response
+
+ #: The debug flag. Set this to `True` to enable debugging of the
+ #: application. In debug mode the debugger will kick in when an unhandled
+ #: exception ocurrs and the integrated server will automatically reload
+ #: the application if changes in the code are detected.
+ #:
+ #: This attribute can also be configured from the config with the `DEBUG`
+ #: configuration key. Defaults to `False`.
+ debug = ConfigAttribute('DEBUG')
+
+ #: The testing flag. Set this to `True` to enable the test mode of
+ #: Flask extensions (and in the future probably also Flask itself).
+ #: For example this might activate unittest helpers that have an
+ #: additional runtime cost which should not be enabled by default.
+ #:
+ #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the
+ #: default it's implicitly enabled.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `TESTING` configuration key. Defaults to `False`.
+ testing = ConfigAttribute('TESTING')
+
+ #: If a secret key is set, cryptographic components can use this to
+ #: sign cookies and other things. Set this to a complex random value
+ #: when you want to use the secure cookie for instance.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `SECRET_KEY` configuration key. Defaults to `None`.
+ secret_key = ConfigAttribute('SECRET_KEY')
+
+ #: The secure cookie uses this for the name of the session cookie.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `SESSION_COOKIE_NAME` configuration key. Defaults to ``'session'``
+ session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME')
+
+ #: A :class:`~datetime.timedelta` which is used to set the expiration
+ #: date of a permanent session. The default is 31 days which makes a
+ #: permanent session survive for roughly one month.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `PERMANENT_SESSION_LIFETIME` configuration key. Defaults to
+ #: ``timedelta(days=31)``
+ permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME',
+ get_converter=_make_timedelta)
+
+ #: Enable this if you want to use the X-Sendfile feature. Keep in
+ #: mind that the server has to support this. This only affects files
+ #: sent with the :func:`send_file` method.
+ #:
+ #: .. versionadded:: 0.2
+ #:
+ #: This attribute can also be configured from the config with the
+ #: `USE_X_SENDFILE` configuration key. Defaults to `False`.
+ use_x_sendfile = ConfigAttribute('USE_X_SENDFILE')
+
+ #: The name of the logger to use. By default the logger name is the
+ #: package name passed to the constructor.
+ #:
+ #: .. versionadded:: 0.4
+ logger_name = ConfigAttribute('LOGGER_NAME')
+
+ #: 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
+ enable_modules = True
+
+ #: The logging format used for the debug logger. This is only used when
+ #: the application is in debug mode, otherwise the attached logging
+ #: handler does the formatting.
+ #:
+ #: .. versionadded:: 0.3
+ debug_log_format = (
+ '-' * 80 + '\n' +
+ '%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n' +
+ '%(message)s\n' +
+ '-' * 80
+ )
+
+ #: Options that are passed directly to the Jinja2 environment.
+ jinja_options = ImmutableDict(
+ extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
+ )
+
+ #: Default configuration parameters.
+ default_config = ImmutableDict({
+ 'DEBUG': False,
+ 'TESTING': False,
+ 'PROPAGATE_EXCEPTIONS': None,
+ 'PRESERVE_CONTEXT_ON_EXCEPTION': None,
+ 'SECRET_KEY': None,
+ 'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
+ 'USE_X_SENDFILE': False,
+ 'LOGGER_NAME': None,
+ 'SERVER_NAME': None,
+ 'APPLICATION_ROOT': None,
+ 'SESSION_COOKIE_NAME': 'session',
+ 'SESSION_COOKIE_DOMAIN': None,
+ 'SESSION_COOKIE_PATH': None,
+ 'SESSION_COOKIE_HTTPONLY': True,
+ 'SESSION_COOKIE_SECURE': False,
+ 'MAX_CONTENT_LENGTH': None,
+ 'TRAP_BAD_REQUEST_ERRORS': False,
+ 'TRAP_HTTP_EXCEPTIONS': False
+ })
+
+ #: The rule object to use for URL rules created. This is used by
+ #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`.
+ #:
+ #: .. versionadded:: 0.7
+ url_rule_class = Rule
+
+ #: the test client that is used with when `test_client` is used.
+ #:
+ #: .. versionadded:: 0.7
+ test_client_class = None
+
+ #: the session interface to use. By default an instance of
+ #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
+ #:
+ #: .. versionadded:: 0.8
+ session_interface = SecureCookieSessionInterface()
+
+ def __init__(self, import_name, static_path=None, static_url_path=None,
+ static_folder='static', template_folder='templates',
+ instance_path=None, instance_relative_config=False):
+ _PackageBoundObject.__init__(self, import_name,
+ template_folder=template_folder)
+ if static_path is not None:
+ from warnings import warn
+ warn(DeprecationWarning('static_path is now called '
+ 'static_url_path'), stacklevel=2)
+ static_url_path = static_path
+
+ if static_url_path is not None:
+ self.static_url_path = static_url_path
+ if static_folder is not None:
+ self.static_folder = static_folder
+ if instance_path is None:
+ instance_path = self.auto_find_instance_path()
+ elif not os.path.isabs(instance_path):
+ raise ValueError('If an instance path is provided it must be '
+ 'absolute. A relative path was given instead.')
+
+ #: Holds the path to the instance folder.
+ #:
+ #: .. versionadded:: 0.8
+ self.instance_path = instance_path
+
+ #: The configuration dictionary as :class:`Config`. This behaves
+ #: exactly like a regular dictionary but supports additional methods
+ #: to load a config from files.
+ self.config = self.make_config(instance_relative_config)
+
+ # Prepare the deferred setup of the logger.
+ self._logger = None
+ self.logger_name = self.import_name
+
+ #: A dictionary of all view functions registered. The keys will
+ #: be function names which are also used to generate URLs and
+ #: the values are the function objects themselves.
+ #: To register a view function, use the :meth:`route` decorator.
+ self.view_functions = {}
+
+ # support for the now deprecated `error_handlers` attribute. The
+ # :attr:`error_handler_spec` shall be used now.
+ self._error_handlers = {}
+
+ #: A dictionary of all registered error handlers. The key is `None`
+ #: 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 `None` points to a list of tuples where the first item
+ #: is the class for the instance check and the second the error handler
+ #: function.
+ #:
+ #: To register a error handler, use the :meth:`errorhandler`
+ #: decorator.
+ self.error_handler_spec = {None: self._error_handlers}
+
+ #: A dictionary with lists of functions that should be called at the
+ #: beginning of the request. The key of the dictionary is the name of
+ #: the blueprint this function is active for, `None` for all requests.
+ #: This can for example be used to open database connections or
+ #: getting hold of the currently logged in user. To register a
+ #: function here, use the :meth:`before_request` decorator.
+ self.before_request_funcs = {}
+
+ #: A lists of functions that should be called at the beginning of the
+ #: first request to this instance. To register a function here, use
+ #: the :meth:`before_first_request` decorator.
+ #:
+ #: .. versionadded:: 0.8
+ self.before_first_request_funcs = []
+
+ #: A dictionary with lists of functions that should be called after
+ #: each request. The key of the dictionary is the name of the blueprint
+ #: this function is active for, `None` for all requests. This can for
+ #: example be used to open database connections or getting hold of the
+ #: currently logged in user. To register a function here, use the
+ #: :meth:`after_request` decorator.
+ self.after_request_funcs = {}
+
+ #: A dictionary with 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,
+ #: `None` 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
+ #: :meth:`teardown_request` decorator.
+ #:
+ #: .. versionadded:: 0.7
+ self.teardown_request_funcs = {}
+
+ #: 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
+ #: `None` 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
+ #:
+ #: .. versionadded:: 0.7
+ self.url_value_preprocessors = {}
+
+ #: A dictionary with lists of functions that can be used as URL value
+ #: preprocessors. The key `None` 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 :meth:`url_defaults` function that adds the parameters
+ #: automatically again that were removed that way.
+ #:
+ #: .. versionadded:: 0.7
+ self.url_default_functions = {}
+
+ #: 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, `None` for all
+ #: requests. Each returns a dictionary that the template context is
+ #: updated with. To register a function here, use the
+ #: :meth:`context_processor` decorator.
+ self.template_context_processors = {
+ None: [_default_template_ctx_processor]
+ }
+
+ #: all the 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.
+ #:
+ #: .. versionadded:: 0.7
+ self.blueprints = {}
+
+ #: a place where extensions can store application specific state. For
+ #: example this is where an extension could store database engines and
+ #: similar things. For backwards compatibility extensions should register
+ #: themselves like this::
+ #:
+ #: if not hasattr(app, 'extensions'):
+ #: app.extensions = {}
+ #: app.extensions['extensionname'] = SomeObject()
+ #:
+ #: The key must match the name of the `flaskext` module. For example in
+ #: case of a "Flask-Foo" extension in `flaskext.foo`, the key would be
+ #: ``'foo'``.
+ #:
+ #: .. versionadded:: 0.7
+ self.extensions = {}
+
+ #: The :class:`~werkzeug.routing.Map` for this instance. You can use
+ #: this to change the routing converters after the class was created
+ #: but before any routes are connected. Example::
+ #:
+ #: from werkzeug.routing import BaseConverter
+ #:
+ #: class ListConverter(BaseConverter):
+ #: def to_python(self, value):
+ #: return value.split(',')
+ #: def to_url(self, values):
+ #: return ','.join(BaseConverter.to_url(value)
+ #: for value in values)
+ #:
+ #: app = Flask(__name__)
+ #: app.url_map.converters['list'] = ListConverter
+ self.url_map = Map()
+
+ # tracks internally if the application already handled at least one
+ # request.
+ self._got_first_request = False
+ self._before_request_lock = Lock()
+
+ # register the static folder for the application. Do that even
+ # if the folder does not exist. First of all it might be created
+ # while the server is running (usually happens during development)
+ # but also because google appengine stores static files somewhere
+ # else when mapped with the .yml file.
+ if self.has_static_folder:
+ self.add_url_rule(self.static_url_path + '/<path:filename>',
+ endpoint='static',
+ view_func=self.send_static_file)
+
+ def _get_error_handlers(self):
+ from warnings import warn
+ warn(DeprecationWarning('error_handlers is deprecated, use the '
+ 'new error_handler_spec attribute instead.'), stacklevel=1)
+ return self._error_handlers
+ def _set_error_handlers(self, value):
+ self._error_handlers = value
+ self.error_handler_spec[None] = value
+ error_handlers = property(_get_error_handlers, _set_error_handlers)
+ del _get_error_handlers, _set_error_handlers
+
+ @locked_cached_property
+ def name(self):
+ """The name of the application. This is usually the import name
+ with the difference that it'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.
+
+ .. versionadded:: 0.8
+ """
+ if self.import_name == '__main__':
+ fn = getattr(sys.modules['__main__'], '__file__', None)
+ if fn is None:
+ return '__main__'
+ return os.path.splitext(os.path.basename(fn))[0]
+ return self.import_name
+
+ @property
+ def propagate_exceptions(self):
+ """Returns the value of the `PROPAGATE_EXCEPTIONS` configuration
+ value in case it's set, otherwise a sensible default is returned.
+
+ .. versionadded:: 0.7
+ """
+ rv = self.config['PROPAGATE_EXCEPTIONS']
+ if rv is not None:
+ return rv
+ return self.testing or self.debug
+
+ @property
+ def preserve_context_on_exception(self):
+ """Returns the value of the `PRESERVE_CONTEXT_ON_EXCEPTION`
+ configuration value in case it's set, otherwise a sensible default
+ is returned.
+
+ .. versionadded:: 0.7
+ """
+ rv = self.config['PRESERVE_CONTEXT_ON_EXCEPTION']
+ if rv is not None:
+ return rv
+ return self.debug
+
+ @property
+ def logger(self):
+ """A :class:`logging.Logger` object for this application. The
+ default configuration is to log to stderr if the application is
+ in debug mode. This logger can be used to (surprise) log messages.
+ Here some examples::
+
+ app.logger.debug('A value for debugging')
+ app.logger.warning('A warning ocurred (%d apples)', 42)
+ app.logger.error('An error occoured')
+
+ .. versionadded:: 0.3
+ """
+ if self._logger and self._logger.name == self.logger_name:
+ return self._logger
+ with _logger_lock:
+ if self._logger and self._logger.name == self.logger_name:
+ return self._logger
+ from flask.logging import create_logger
+ self._logger = rv = create_logger(self)
+ return rv
+
+ @locked_cached_property
+ def jinja_env(self):
+ """The Jinja2 environment used to load templates."""
+ rv = self.create_jinja_environment()
+
+ # Hack to support the init_jinja_globals method which is supported
+ # until 1.0 but has an API deficiency.
+ if getattr(self.init_jinja_globals, 'im_func', None) is not \
+ Flask.init_jinja_globals.im_func:
+ from warnings import warn
+ warn(DeprecationWarning('This flask class uses a customized '
+ 'init_jinja_globals() method which is deprecated. '
+ 'Move the code from that method into the '
+ 'create_jinja_environment() method instead.'))
+ self.__dict__['jinja_env'] = rv
+ self.init_jinja_globals()
+
+ return rv
+
+ @property
+ def got_first_request(self):
+ """This attribute is set to `True` if the application started
+ handling the first request.
+
+ .. versionadded:: 0.8
+ """
+ return self._got_first_request
+
+ def make_config(self, instance_relative=False):
+ """Used to create the config attribute by the Flask constructor.
+ The `instance_relative` parameter is passed in from the constructor
+ of Flask (there named `instance_relative_config`) and indicates if
+ the config should be relative to the instance path or the root path
+ of the application.
+
+ .. versionadded:: 0.8
+ """
+ root_path = self.root_path
+ if instance_relative:
+ root_path = self.instance_path
+ return Config(root_path, self.default_config)
+
+ def auto_find_instance_path(self):
+ """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 ``instance`` next to your main file or
+ the package.
+
+ .. versionadded:: 0.8
+ """
+ prefix, package_path = find_package(self.import_name)
+ if prefix is None:
+ return os.path.join(package_path, 'instance')
+ return os.path.join(prefix, 'var', self.name + '-instance')
+
+ def open_instance_resource(self, resource, mode='rb'):
+ """Opens a resource from the application's instance folder
+ (:attr:`instance_path`). Otherwise works like
+ :meth:`open_resource`. Instance resources can also be opened for
+ writing.
+
+ :param resource: the name of the resource. To access resources within
+ subfolders use forward slashes as separator.
+ """
+ return open(os.path.join(self.instance_path, resource), mode)
+
+ def create_jinja_environment(self):
+ """Creates the Jinja2 environment based on :attr:`jinja_options`
+ and :meth:`select_jinja_autoescape`. Since 0.7 this also adds
+ the Jinja2 globals and filters after initialization. Override
+ this function to customize the behavior.
+
+ .. versionadded:: 0.5
+ """
+ options = dict(self.jinja_options)
+ if 'autoescape' not in options:
+ options['autoescape'] = self.select_jinja_autoescape
+ rv = Environment(self, **options)
+ rv.globals.update(
+ url_for=url_for,
+ get_flashed_messages=get_flashed_messages
+ )
+ rv.filters['tojson'] = _tojson_filter
+ return rv
+
+ def create_global_jinja_loader(self):
+ """Creates the loader for the Jinja2 environment. Can be used to
+ override just the loader and keeping the rest unchanged. It's
+ discouraged to override this function. Instead one should override
+ the :meth:`jinja_loader` function instead.
+
+ The global loader dispatches between the loaders of the application
+ and the individual blueprints.
+
+ .. versionadded:: 0.7
+ """
+ return DispatchingJinjaLoader(self)
+
+ def init_jinja_globals(self):
+ """Deprecated. Used to initialize the Jinja2 globals.
+
+ .. versionadded:: 0.5
+ .. versionchanged:: 0.7
+ This method is deprecated with 0.7. Override
+ :meth:`create_jinja_environment` instead.
+ """
+
+ def select_jinja_autoescape(self, filename):
+ """Returns `True` if autoescaping should be active for the given
+ template name.
+
+ .. versionadded:: 0.5
+ """
+ if filename is None:
+ return False
+ return filename.endswith(('.html', '.htm', '.xml', '.xhtml'))
+
+ def update_template_context(self, context):
+ """Update the template context with some commonly used variables.
+ This injects request, session, config and g into the template
+ context as well as everything template context processors want
+ to inject. Note that the as of Flask 0.6, the original values
+ in the context will not be overriden if a context processor
+ decides to return a value with the same key.
+
+ :param context: the context as a dictionary that is updated in place
+ to add extra variables.
+ """
+ funcs = self.template_context_processors[None]
+ bp = _request_ctx_stack.top.request.blueprint
+ if bp is not None and bp in self.template_context_processors:
+ funcs = chain(funcs, self.template_context_processors[bp])
+ orig_ctx = context.copy()
+ for func in funcs:
+ context.update(func())
+ # make sure the original values win. This makes it possible to
+ # easier add new variables in context processors without breaking
+ # existing views.
+ context.update(orig_ctx)
+
+ def run(self, host='127.0.0.1', port=5000, debug=None, **options):
+ """Runs the application on a local development server. If the
+ :attr:`debug` flag is set the server will automatically reload
+ for code changes and show a debugger in case an exception happened.
+
+ If you want to run the application in debug mode, but disable the
+ code execution on the interactive debugger, you can pass
+ ``use_evalex=False`` as parameter. This will keep the debugger's
+ traceback screen active, but disable code execution.
+
+ .. admonition:: Keep in Mind
+
+ Flask will suppress any server error with a generic error page
+ unless it is in debug mode. As such to enable just the
+ interactive debugger without the code reloading, you have to
+ invoke :meth:`run` with ``debug=True`` and ``use_reloader=False``.
+ Setting ``use_debugger`` to `True` without being in debug mode
+ won't catch any exceptions because there won't be any to
+ catch.
+
+ :param host: the hostname to listen on. set this to ``'0.0.0.0'``
+ to have the server available externally as well.
+ :param port: the port of the webserver
+ :param debug: if given, enable or disable debug mode.
+ See :attr:`debug`.
+ :param options: the options to be forwarded to the underlying
+ Werkzeug server. See
+ :func:`werkzeug.serving.run_simple` for more
+ information.
+ """
+ from werkzeug.serving import run_simple
+ if debug is not None:
+ self.debug = bool(debug)
+ options.setdefault('use_reloader', self.debug)
+ options.setdefault('use_debugger', self.debug)
+ try:
+ run_simple(host, port, self, **options)
+ finally:
+ # reset the first request information if the development server
+ # resetted normally. This makes it possible to restart the server
+ # without reloader and that stuff from an interactive shell.
+ self._got_first_request = False
+
+ def test_client(self, use_cookies=True):
+ """Creates a test client for this application. For information
+ about unit testing head over to :ref:`testing`.
+
+ The test client can be used in a `with` block to defer the closing down
+ of the context until the end of the `with` block. This is useful if
+ you want to access the context locals for testing::
+
+ with app.test_client() as c:
+ rv = c.get('/?vodka=42')
+ assert request.args['vodka'] == '42'
+
+ See :class:`~flask.testing.FlaskClient` for more information.
+
+ .. versionchanged:: 0.4
+ added support for `with` block usage for the client.
+
+ .. versionadded:: 0.7
+ The `use_cookies` parameter was added as well as the ability
+ to override the client to be used by setting the
+ :attr:`test_client_class` attribute.
+ """
+ cls = self.test_client_class
+ if cls is None:
+ from flask.testing import FlaskClient as cls
+ return cls(self, self.response_class, use_cookies=use_cookies)
+
+ def open_session(self, request):
+ """Creates or opens a new session. Default implementation stores all
+ session data in a signed cookie. This requires that the
+ :attr:`secret_key` is set. Instead of overriding this method
+ we recommend replacing the :class:`session_interface`.
+
+ :param request: an instance of :attr:`request_class`.
+ """
+ return self.session_interface.open_session(self, request)
+
+ def save_session(self, session, response):
+ """Saves the session if it needs updates. For the default
+ implementation, check :meth:`open_session`. Instead of overriding this
+ method we recommend replacing the :class:`session_interface`.
+
+ :param session: the session to be saved (a
+ :class:`~werkzeug.contrib.securecookie.SecureCookie`
+ object)
+ :param response: an instance of :attr:`response_class`
+ """
+ return self.session_interface.save_session(self, session, response)
+
+ def make_null_session(self):
+ """Creates a new instance of a missing session. Instead of overriding
+ this method we recommend replacing the :class:`session_interface`.
+
+ .. versionadded:: 0.7
+ """
+ return self.session_interface.make_null_session(self)
+
+ def register_module(self, module, **options):
+ """Registers a module with this application. The keyword argument
+ of this function are the same as the ones for the constructor of the
+ :class:`Module` class and will override the values of the module if
+ provided.
+
+ .. versionchanged:: 0.7
+ The module system was deprecated in favor for the blueprint
+ system.
+ """
+ assert blueprint_is_module(module), 'register_module requires ' \
+ 'actual module objects. Please upgrade to blueprints though.'
+ if not self.enable_modules:
+ raise RuntimeError('Module support was disabled but code '
+ 'attempted to register a module named %r' % module)
+ else:
+ from warnings import warn
+ warn(DeprecationWarning('Modules are deprecated. Upgrade to '
+ 'using blueprints. Have a look into the documentation for '
+ 'more information. If this module was registered by a '
+ 'Flask-Extension upgrade the extension or contact the author '
+ 'of that extension instead. (Registered %r)' % module),
+ stacklevel=2)
+
+ self.register_blueprint(module, **options)
+
+ @setupmethod
+ def register_blueprint(self, blueprint, **options):
+ """Registers a blueprint on the application.
+
+ .. versionadded:: 0.7
+ """
+ first_registration = False
+ if blueprint.name in self.blueprints:
+ assert self.blueprints[blueprint.name] is blueprint, \
+ 'A blueprint\'s name collision ocurred between %r and ' \
+ '%r. Both share the same name "%s". Blueprints that ' \
+ 'are created on the fly need unique names.' % \
+ (blueprint, self.blueprints[blueprint.name], blueprint.name)
+ else:
+ self.blueprints[blueprint.name] = blueprint
+ first_registration = True
+ blueprint.register(self, options, first_registration)
+
+ @setupmethod
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """Connects a URL rule. Works exactly like the :meth:`route`
+ decorator. If a view_func is provided it will be registered with the
+ endpoint.
+
+ Basically this example::
+
+ @app.route('/')
+ def index():
+ pass
+
+ Is equivalent to the following::
+
+ def index():
+ pass
+ app.add_url_rule('/', 'index', index)
+
+ If the view_func is not provided you will need to connect the endpoint
+ to a view function like so::
+
+ app.view_functions['index'] = index
+
+ Internally :meth:`route` invokes :meth:`add_url_rule` so if you want
+ to customize the behavior via subclassing you only need to change
+ this method.
+
+ For more information refer to :ref:`url-route-registrations`.
+
+ .. versionchanged:: 0.2
+ `view_func` parameter added.
+
+ .. versionchanged:: 0.6
+ `OPTIONS` is added automatically as method.
+
+ :param rule: the URL rule as string
+ :param endpoint: the endpoint for the registered URL rule. Flask
+ itself assumes the name of the view function as
+ endpoint
+ :param view_func: the function to call when serving a request to the
+ provided endpoint
+ :param options: the options to be forwarded to the underlying
+ :class:`~werkzeug.routing.Rule` object. A change
+ to Werkzeug is handling of method options. methods
+ is a list of methods this rule should be limited
+ to (`GET`, `POST` etc.). By default a rule
+ just listens for `GET` (and implicitly `HEAD`).
+ Starting with Flask 0.6, `OPTIONS` is implicitly
+ added and handled by the standard request handling.
+ """
+ if endpoint is None:
+ endpoint = _endpoint_from_view_func(view_func)
+ options['endpoint'] = endpoint
+ methods = options.pop('methods', None)
+
+ # if the methods are not given and the view_func object knows its
+ # methods we can use that instead. If neither exists, we go with
+ # a tuple of only `GET` as default.
+ if methods is None:
+ methods = getattr(view_func, 'methods', None) or ('GET',)
+
+ # starting with Flask 0.8 the view_func object can disable and
+ # force-enable the automatic options handling.
+ provide_automatic_options = getattr(view_func,
+ 'provide_automatic_options', None)
+
+ if provide_automatic_options is None:
+ if 'OPTIONS' not in methods:
+ methods = tuple(methods) + ('OPTIONS',)
+ provide_automatic_options = True
+ else:
+ provide_automatic_options = False
+
+ # due to a werkzeug bug we need to make sure that the defaults are
+ # None if they are an empty dictionary. This should not be necessary
+ # with Werkzeug 0.7
+ options['defaults'] = options.get('defaults') or None
+
+ rule = self.url_rule_class(rule, methods=methods, **options)
+ rule.provide_automatic_options = provide_automatic_options
+ self.url_map.add(rule)
+ if view_func is not None:
+ self.view_functions[endpoint] = view_func
+
+ def route(self, rule, **options):
+ """A decorator that is used to register a view function for a
+ given URL rule. This does the same thing as :meth:`add_url_rule`
+ but is intended for decorator usage::
+
+ @app.route('/')
+ def index():
+ return 'Hello World'
+
+ For more information refer to :ref:`url-route-registrations`.
+
+ :param rule: the URL rule as string
+ :param endpoint: the endpoint for the registered URL rule. Flask
+ itself assumes the name of the view function as
+ endpoint
+ :param view_func: the function to call when serving a request to the
+ provided endpoint
+ :param options: the options to be forwarded to the underlying
+ :class:`~werkzeug.routing.Rule` object. A change
+ to Werkzeug is handling of method options. methods
+ is a list of methods this rule should be limited
+ to (`GET`, `POST` etc.). By default a rule
+ just listens for `GET` (and implicitly `HEAD`).
+ Starting with Flask 0.6, `OPTIONS` is implicitly
+ added and handled by the standard request handling.
+ """
+ def decorator(f):
+ endpoint = options.pop('endpoint', None)
+ self.add_url_rule(rule, endpoint, f, **options)
+ return f
+ return decorator
+
+ @setupmethod
+ def endpoint(self, endpoint):
+ """A decorator to register a function as an endpoint.
+ Example::
+
+ @app.endpoint('example.endpoint')
+ def example():
+ return "example"
+
+ :param endpoint: the name of the endpoint
+ """
+ def decorator(f):
+ self.view_functions[endpoint] = f
+ return f
+ return decorator
+
+ @setupmethod
+ def errorhandler(self, code_or_exception):
+ """A decorator that is used to register a function give a given
+ error code. Example::
+
+ @app.errorhandler(404)
+ def page_not_found(error):
+ return 'This page does not exist', 404
+
+ You can also register handlers for arbitrary exceptions::
+
+ @app.errorhandler(DatabaseError)
+ def special_exception_handler(error):
+ return 'Database connection failed', 500
+
+ You can also register a function as error handler without using
+ the :meth:`errorhandler` decorator. The following example is
+ equivalent to the one above::
+
+ def page_not_found(error):
+ return 'This page does not exist', 404
+ app.error_handler_spec[None][404] = page_not_found
+
+ Setting error handlers via assignments to :attr:`error_handler_spec`
+ however is discouraged as it requires fidling with nested dictionaries
+ and the special case for arbitrary exception types.
+
+ The first `None` refers to the active blueprint. If the error
+ handler should be application wide `None` shall be used.
+
+ .. versionadded:: 0.7
+ One can now additionally also register custom exception types
+ that do not necessarily have to be a subclass of the
+ :class:`~werkzeug.exceptions.HTTPException` class.
+
+ :param code: the code as integer for the handler
+ """
+ def decorator(f):
+ self._register_error_handler(None, code_or_exception, f)
+ return f
+ return decorator
+
+ def register_error_handler(self, code_or_exception, f):
+ """Alternative error attach function to the :meth:`errorhandler`
+ decorator that is more straightforward to use for non decorator
+ usage.
+
+ .. versionadded:: 0.7
+ """
+ self._register_error_handler(None, code_or_exception, f)
+
+ @setupmethod
+ def _register_error_handler(self, key, code_or_exception, f):
+ if isinstance(code_or_exception, HTTPException):
+ code_or_exception = code_or_exception.code
+ if isinstance(code_or_exception, (int, long)):
+ assert code_or_exception != 500 or key is None, \
+ 'It is currently not possible to register a 500 internal ' \
+ 'server error on a per-blueprint level.'
+ self.error_handler_spec.setdefault(key, {})[code_or_exception] = f
+ else:
+ self.error_handler_spec.setdefault(key, {}).setdefault(None, []) \
+ .append((code_or_exception, f))
+
+ @setupmethod
+ def template_filter(self, name=None):
+ """A decorator that is used to register custom template filter.
+ You can specify a name for the filter, otherwise the function
+ name will be used. Example::
+
+ @app.template_filter()
+ def reverse(s):
+ return s[::-1]
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ def decorator(f):
+ self.jinja_env.filters[name or f.__name__] = f
+ return f
+ return decorator
+
+ @setupmethod
+ def before_request(self, f):
+ """Registers a function to run before each request."""
+ self.before_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def before_first_request(self, f):
+ """Registers a function to be run before the first request to this
+ instance of the application.
+
+ .. versionadded:: 0.8
+ """
+ self.before_first_request_funcs.append(f)
+
+ @setupmethod
+ def after_request(self, f):
+ """Register a function to be run after each request. Your function
+ must take one parameter, a :attr:`response_class` object and return
+ a new response object or the same (see :meth:`process_response`).
+
+ As of Flask 0.7 this function might not be executed at the end of the
+ request in case an unhandled exception ocurred.
+ """
+ self.after_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def teardown_request(self, f):
+ """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.
+
+ Example::
+
+ ctx = app.test_request_context()
+ ctx.push()
+ ...
+ ctx.pop()
+
+ When ``ctx.pop()`` 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.
+
+ 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.
+ """
+ self.teardown_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def context_processor(self, f):
+ """Registers a template context processor function."""
+ self.template_context_processors[None].append(f)
+ return f
+
+ @setupmethod
+ def url_value_preprocessor(self, f):
+ """Registers a function as URL value preprocessor for all view
+ functions of the application. It's called before the view functions
+ are called and can modify the url values provided.
+ """
+ self.url_value_preprocessors.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def url_defaults(self, f):
+ """Callback function for URL defaults for all view functions of the
+ application. It's called with the endpoint and values and should
+ update the values passed in place.
+ """
+ self.url_default_functions.setdefault(None, []).append(f)
+ return f
+
+ def handle_http_exception(self, e):
+ """Handles an HTTP exception. By default this will invoke the
+ registered error handlers and fall back to returning the
+ exception as response.
+
+ .. versionadded: 0.3
+ """
+ handlers = self.error_handler_spec.get(request.blueprint)
+ if handlers and e.code in handlers:
+ handler = handlers[e.code]
+ else:
+ handler = self.error_handler_spec[None].get(e.code)
+ if handler is None:
+ return e
+ return handler(e)
+
+ def trap_http_exception(self, e):
+ """Checks if an HTTP exception should be trapped or not. By default
+ this will return `False` for all exceptions except for a bad request
+ key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to `True`. It
+ also returns `True` if ``TRAP_HTTP_EXCEPTIONS`` is set to `True`.
+
+ This is called for all HTTP exceptions raised by a view function.
+ If it returns `True` 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.
+
+ .. versionadded:: 0.8
+ """
+ if self.config['TRAP_HTTP_EXCEPTIONS']:
+ return True
+ if self.config['TRAP_BAD_REQUEST_ERRORS']:
+ return isinstance(e, BadRequest)
+ return False
+
+ def handle_user_exception(self, e):
+ """This method is called whenever an exception occurs that should be
+ handled. A special case are
+ :class:`~werkzeug.exception.HTTPException`\s which are forwarded by
+ this function to the :meth:`handle_http_exception` method. This
+ function will either return a response value or reraise the
+ exception with the same traceback.
+
+ .. versionadded:: 0.7
+ """
+ exc_type, exc_value, tb = sys.exc_info()
+ assert exc_value is e
+
+ # ensure not to trash sys.exc_info() at that point in case someone
+ # wants the traceback preserved in handle_http_exception. Of course
+ # we cannot prevent users from trashing it themselves in a custom
+ # trap_http_exception method so that's their fault then.
+ if isinstance(e, HTTPException) and not self.trap_http_exception(e):
+ return self.handle_http_exception(e)
+
+ blueprint_handlers = ()
+ handlers = self.error_handler_spec.get(request.blueprint)
+ if handlers is not None:
+ blueprint_handlers = handlers.get(None, ())
+ app_handlers = self.error_handler_spec[None].get(None, ())
+ for typecheck, handler in chain(blueprint_handlers, app_handlers):
+ if isinstance(e, typecheck):
+ return handler(e)
+
+ raise exc_type, exc_value, tb
+
+ def handle_exception(self, e):
+ """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.
+
+ .. versionadded: 0.3
+ """
+ exc_type, exc_value, tb = sys.exc_info()
+
+ got_request_exception.send(self, exception=e)
+ handler = self.error_handler_spec[None].get(500)
+
+ if self.propagate_exceptions:
+ # if we want to repropagate the exception, we can attempt to
+ # raise it with the whole traceback in case we can do that
+ # (the function was actually called from the except part)
+ # otherwise, we just raise the error again
+ if exc_value is e:
+ raise exc_type, exc_value, tb
+ else:
+ raise e
+
+ self.log_exception((exc_type, exc_value, tb))
+ if handler is None:
+ return InternalServerError()
+ return handler(e)
+
+ def log_exception(self, exc_info):
+ """Logs an exception. This is called by :meth:`handle_exception`
+ if debugging is disabled and right before the handler is called.
+ The default implementation logs the exception as error on the
+ :attr:`logger`.
+
+ .. versionadded:: 0.8
+ """
+ self.logger.error('Exception on %s [%s]' % (
+ request.path,
+ request.method
+ ), exc_info=exc_info)
+
+ def raise_routing_exception(self, request):
+ """Exceptions that are recording during routing are reraised with
+ this method. During debug we are not reraising redirect requests
+ for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising
+ a different error instead to help debug situations.
+
+ :internal:
+ """
+ if not self.debug \
+ or not isinstance(request.routing_exception, RequestRedirect) \
+ or request.method in ('GET', 'HEAD', 'OPTIONS'):
+ raise request.routing_exception
+
+ from .debughelpers import FormDataRoutingRedirect
+ raise FormDataRoutingRedirect(request)
+
+ def dispatch_request(self):
+ """Does the request dispatching. Matches the URL and returns the
+ return value of the view or error handler. This does not have to
+ be a response object. In order to convert the return value to a
+ proper response object, call :func:`make_response`.
+
+ .. versionchanged:: 0.7
+ This no longer does the exception handling, this code was
+ moved to the new :meth:`full_dispatch_request`.
+ """
+ req = _request_ctx_stack.top.request
+ if req.routing_exception is not None:
+ self.raise_routing_exception(req)
+ rule = req.url_rule
+ # if we provide automatic options for this URL and the
+ # request came with the OPTIONS method, reply automatically
+ if getattr(rule, 'provide_automatic_options', False) \
+ and req.method == 'OPTIONS':
+ return self.make_default_options_response()
+ # otherwise dispatch to the handler for that endpoint
+ return self.view_functions[rule.endpoint](**req.view_args)
+
+ def full_dispatch_request(self):
+ """Dispatches the request and on top of that performs request
+ pre and postprocessing as well as HTTP exception catching and
+ error handling.
+
+ .. versionadded:: 0.7
+ """
+ self.try_trigger_before_first_request_functions()
+ try:
+ request_started.send(self)
+ rv = self.preprocess_request()
+ if rv is None:
+ rv = self.dispatch_request()
+ except Exception, e:
+ rv = self.handle_user_exception(e)
+ response = self.make_response(rv)
+ response = self.process_response(response)
+ request_finished.send(self, response=response)
+ return response
+
+ def try_trigger_before_first_request_functions(self):
+ """Called before each request and will ensure that it triggers
+ the :attr:`before_first_request_funcs` and only exactly once per
+ application instance (which means process usually).
+
+ :internal:
+ """
+ if self._got_first_request:
+ return
+ with self._before_request_lock:
+ if self._got_first_request:
+ return
+ self._got_first_request = True
+ for func in self.before_first_request_funcs:
+ func()
+
+ def make_default_options_response(self):
+ """This method is called to create the default `OPTIONS` response.
+ This can be changed through subclassing to change the default
+ behaviour of `OPTIONS` responses.
+
+ .. versionadded:: 0.7
+ """
+ adapter = _request_ctx_stack.top.url_adapter
+ if hasattr(adapter, 'allowed_methods'):
+ methods = adapter.allowed_methods()
+ else:
+ # fallback for Werkzeug < 0.7
+ methods = []
+ try:
+ adapter.match(method='--')
+ except MethodNotAllowed, e:
+ methods = e.valid_methods
+ except HTTPException, e:
+ pass
+ rv = self.response_class()
+ rv.allow.update(methods)
+ return rv
+
+ def make_response(self, rv):
+ """Converts the return value from a view function to a real
+ response object that is an instance of :attr:`response_class`.
+
+ The following types are allowed for `rv`:
+
+ .. tabularcolumns:: |p{3.5cm}|p{9.5cm}|
+
+ ======================= ===========================================
+ :attr:`response_class` the object is returned unchanged
+ :class:`str` a response object is created with the
+ string as body
+ :class:`unicode` a response object is created with the
+ string encoded to utf-8 as body
+ :class:`tuple` the response object is created with the
+ contents of the tuple as arguments
+ a WSGI function the function is called as WSGI application
+ and buffered as response object
+ ======================= ===========================================
+
+ :param rv: the return value from the view function
+ """
+ if rv is None:
+ raise ValueError('View function did not return a response')
+ if isinstance(rv, self.response_class):
+ return rv
+ if isinstance(rv, basestring):
+ return self.response_class(rv)
+ if isinstance(rv, tuple):
+ return self.response_class(*rv)
+ return self.response_class.force_type(rv, request.environ)
+
+ def create_url_adapter(self, request):
+ """Creates a URL adapter for the given request. The URL adapter
+ is created at a point where the request context is not yet set up
+ so the request is passed explicitly.
+
+ .. versionadded:: 0.6
+ """
+ return self.url_map.bind_to_environ(request.environ,
+ server_name=self.config['SERVER_NAME'])
+
+ def inject_url_defaults(self, endpoint, values):
+ """Injects the URL defaults for the given endpoint directly into
+ the values dictionary passed. This is used internally and
+ automatically called on URL building.
+
+ .. versionadded:: 0.7
+ """
+ funcs = self.url_default_functions.get(None, ())
+ if '.' in endpoint:
+ bp = endpoint.split('.', 1)[0]
+ funcs = chain(funcs, self.url_default_functions.get(bp, ()))
+ for func in funcs:
+ func(endpoint, values)
+
+ def preprocess_request(self):
+ """Called before the actual request dispatching and will
+ call every as :meth:`before_request` decorated function.
+ If any of these function returns a value it's handled as
+ if it was the return value from the view and further
+ request handling is stopped.
+
+ This also triggers the :meth:`url_value_processor` functions before
+ the actualy :meth:`before_request` functions are called.
+ """
+ bp = _request_ctx_stack.top.request.blueprint
+
+ funcs = self.url_value_preprocessors.get(None, ())
+ if bp is not None and bp in self.url_value_preprocessors:
+ funcs = chain(funcs, self.url_value_preprocessors[bp])
+ for func in funcs:
+ func(request.endpoint, request.view_args)
+
+ funcs = self.before_request_funcs.get(None, ())
+ if bp is not None and bp in self.before_request_funcs:
+ funcs = chain(funcs, self.before_request_funcs[bp])
+ for func in funcs:
+ rv = func()
+ if rv is not None:
+ return rv
+
+ def process_response(self, response):
+ """Can be overridden in order to modify the response object
+ before it's sent to the WSGI server. By default this will
+ call all the :meth:`after_request` decorated functions.
+
+ .. versionchanged:: 0.5
+ As of Flask 0.5 the functions registered for after request
+ execution are called in reverse order of registration.
+
+ :param response: a :attr:`response_class` object.
+ :return: a new response object or the same, has to be an
+ instance of :attr:`response_class`.
+ """
+ ctx = _request_ctx_stack.top
+ bp = ctx.request.blueprint
+ if not self.session_interface.is_null_session(ctx.session):
+ self.save_session(ctx.session, response)
+ funcs = ()
+ if bp is not None and bp in self.after_request_funcs:
+ funcs = reversed(self.after_request_funcs[bp])
+ if None in self.after_request_funcs:
+ funcs = chain(funcs, reversed(self.after_request_funcs[None]))
+ for handler in funcs:
+ response = handler(response)
+ return response
+
+ def do_teardown_request(self):
+ """Called after the actual request dispatching and will
+ call every as :meth:`teardown_request` decorated function. This is
+ not actually called by the :class:`Flask` 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.
+ """
+ funcs = reversed(self.teardown_request_funcs.get(None, ()))
+ bp = _request_ctx_stack.top.request.blueprint
+ if bp is not None and bp in self.teardown_request_funcs:
+ funcs = chain(funcs, reversed(self.teardown_request_funcs[bp]))
+ exc = sys.exc_info()[1]
+ for func in funcs:
+ rv = func(exc)
+ if rv is not None:
+ return rv
+ request_tearing_down.send(self)
+
+ def request_context(self, environ):
+ """Creates a :class:`~flask.ctx.RequestContext` from the given
+ environment and binds it to the current context. This must be used in
+ combination with the `with` statement because the request is only bound
+ to the current context for the duration of the `with` block.
+
+ Example usage::
+
+ with app.request_context(environ):
+ do_something_with(request)
+
+ The object returned can also be used without the `with` statement
+ which is useful for working in the shell. The example above is
+ doing exactly the same as this code::
+
+ ctx = app.request_context(environ)
+ ctx.push()
+ try:
+ do_something_with(request)
+ finally:
+ ctx.pop()
+
+ .. versionchanged:: 0.3
+ Added support for non-with statement usage and `with` statement
+ is now passed the ctx object.
+
+ :param environ: a WSGI environment
+ """
+ return RequestContext(self, environ)
+
+ def test_request_context(self, *args, **kwargs):
+ """Creates a WSGI environment from the given values (see
+ :func:`werkzeug.test.EnvironBuilder` for more information, this
+ function accepts the same arguments).
+ """
+ from flask.testing import make_test_environ_builder
+ builder = make_test_environ_builder(self, *args, **kwargs)
+ try:
+ return self.request_context(builder.get_environ())
+ finally:
+ builder.close()
+
+ def wsgi_app(self, environ, start_response):
+ """The actual WSGI application. This is not implemented in
+ `__call__` so that middlewares can be applied without losing a
+ reference to the class. So instead of doing this::
+
+ app = MyMiddleware(app)
+
+ It's a better idea to do this instead::
+
+ app.wsgi_app = MyMiddleware(app.wsgi_app)
+
+ Then you still have the original application object around and
+ can continue to call methods on it.
+
+ .. versionchanged:: 0.7
+ 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 :ref:`callbacks-and-errors`.
+
+ :param environ: a WSGI environment
+ :param start_response: a callable accepting a status code,
+ a list of headers and an optional
+ exception context to start the response
+ """
+ with self.request_context(environ):
+ try:
+ response = self.full_dispatch_request()
+ except Exception, e:
+ response = self.make_response(self.handle_exception(e))
+ return response(environ, start_response)
+
+ @property
+ def modules(self):
+ from warnings import warn
+ warn(DeprecationWarning('Flask.modules is deprecated, use '
+ 'Flask.blueprints instead'), stacklevel=2)
+ return self.blueprints
+
+ def __call__(self, environ, start_response):
+ """Shortcut for :attr:`wsgi_app`."""
+ return self.wsgi_app(environ, start_response)
diff --git a/websdk/flask/blueprints.py b/websdk/flask/blueprints.py
new file mode 100644
index 0000000..ccdda38
--- /dev/null
+++ b/websdk/flask/blueprints.py
@@ -0,0 +1,321 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.blueprints
+ ~~~~~~~~~~~~~~~~
+
+ Blueprints are the recommended way to implement larger or more
+ pluggable applications in Flask 0.7 and later.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from functools import update_wrapper
+
+from .helpers import _PackageBoundObject, _endpoint_from_view_func
+
+
+class BlueprintSetupState(object):
+ """Temporary holder object for registering a blueprint with the
+ application. An instance of this class is created by the
+ :meth:`~flask.Blueprint.make_setup_state` method and later passed
+ to all register callback functions.
+ """
+
+ def __init__(self, blueprint, app, options, first_registration):
+ #: a reference to the current application
+ self.app = app
+
+ #: a reference to the blurprint that created this setup state.
+ self.blueprint = blueprint
+
+ #: a dictionary with all options that were passed to the
+ #: :meth:`~flask.Flask.register_blueprint` method.
+ self.options = options
+
+ #: 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.
+ self.first_registration = first_registration
+
+ subdomain = self.options.get('subdomain')
+ if subdomain is None:
+ subdomain = self.blueprint.subdomain
+
+ #: The subdomain that the blueprint should be active for, `None`
+ #: otherwise.
+ self.subdomain = subdomain
+
+ url_prefix = self.options.get('url_prefix')
+ if url_prefix is None:
+ url_prefix = self.blueprint.url_prefix
+
+ #: The prefix that should be used for all URLs defined on the
+ #: blueprint.
+ self.url_prefix = url_prefix
+
+ #: A dictionary with URL defaults that is added to each and every
+ #: URL that was defined with the blueprint.
+ self.url_defaults = dict(self.blueprint.url_values_defaults)
+ self.url_defaults.update(self.options.get('url_defaults', ()))
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """A helper method to register a rule (and optionally a view function)
+ to the application. The endpoint is automatically prefixed with the
+ blueprint's name.
+ """
+ if self.url_prefix:
+ rule = self.url_prefix + rule
+ options.setdefault('subdomain', self.subdomain)
+ if endpoint is None:
+ endpoint = _endpoint_from_view_func(view_func)
+ defaults = self.url_defaults
+ if 'defaults' in options:
+ defaults = dict(defaults, **options.pop('defaults'))
+ self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
+ view_func, defaults=defaults, **options)
+
+
+class Blueprint(_PackageBoundObject):
+ """Represents a blueprint. A blueprint is an object that records
+ functions that will be called with the
+ :class:`~flask.blueprint.BlueprintSetupState` later to register functions
+ or other things on the main application. See :ref:`blueprints` for more
+ information.
+
+ .. versionadded:: 0.7
+ """
+
+ warn_on_modifications = False
+ _got_registered_once = False
+
+ def __init__(self, name, import_name, static_folder=None,
+ static_url_path=None, template_folder=None,
+ url_prefix=None, subdomain=None, url_defaults=None):
+ _PackageBoundObject.__init__(self, import_name, template_folder)
+ self.name = name
+ self.url_prefix = url_prefix
+ self.subdomain = subdomain
+ self.static_folder = static_folder
+ self.static_url_path = static_url_path
+ self.deferred_functions = []
+ self.view_functions = {}
+ if url_defaults is None:
+ url_defaults = {}
+ self.url_values_defaults = url_defaults
+
+ def record(self, func):
+ """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 :meth:`make_setup_state`
+ method.
+ """
+ if self._got_registered_once and self.warn_on_modifications:
+ from warnings import warn
+ warn(Warning('The blueprint was already registered once '
+ 'but is getting modified now. These changes '
+ 'will not show up.'))
+ self.deferred_functions.append(func)
+
+ def record_once(self, func):
+ """Works like :meth:`record` 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.
+ """
+ def wrapper(state):
+ if state.first_registration:
+ func(state)
+ return self.record(update_wrapper(wrapper, func))
+
+ def make_setup_state(self, app, options, first_registration=False):
+ """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
+ object that is later passed to the register callback functions.
+ Subclasses can override this to return a subclass of the setup state.
+ """
+ return BlueprintSetupState(self, app, options, first_registration)
+
+ def register(self, app, options, first_registration=False):
+ """Called by :meth:`Flask.register_blueprint` to register a blueprint
+ on the application. This can be overridden to customize the register
+ behavior. Keyword arguments from
+ :func:`~flask.Flask.register_blueprint` are directly forwarded to this
+ method in the `options` dictionary.
+ """
+ self._got_registered_once = True
+ state = self.make_setup_state(app, options, first_registration)
+ if self.has_static_folder:
+ state.add_url_rule(self.static_url_path + '/<path:filename>',
+ view_func=self.send_static_file,
+ endpoint='static')
+
+ for deferred in self.deferred_functions:
+ deferred(state)
+
+ def route(self, rule, **options):
+ """Like :meth:`Flask.route` but for a blueprint. The endpoint for the
+ :func:`url_for` function is prefixed with the name of the blueprint.
+ """
+ def decorator(f):
+ endpoint = options.pop("endpoint", f.__name__)
+ self.add_url_rule(rule, endpoint, f, **options)
+ return f
+ return decorator
+
+ def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
+ """Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
+ the :func:`url_for` function is prefixed with the name of the blueprint.
+ """
+ if endpoint:
+ assert '.' not in endpoint, "Blueprint endpoint's should not contain dot's"
+ self.record(lambda s:
+ s.add_url_rule(rule, endpoint, view_func, **options))
+
+ def endpoint(self, endpoint):
+ """Like :meth:`Flask.endpoint` 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 `.` it will be registered to the current blueprint, otherwise
+ it's an application independent endpoint.
+ """
+ def decorator(f):
+ def register_endpoint(state):
+ state.app.view_functions[endpoint] = f
+ self.record_once(register_endpoint)
+ return f
+ return decorator
+
+ def before_request(self, f):
+ """Like :meth:`Flask.before_request` but for a blueprint. This function
+ is only executed before each request that is handled by a function of
+ that blueprint.
+ """
+ self.record_once(lambda s: s.app.before_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def before_app_request(self, f):
+ """Like :meth:`Flask.before_request`. Such a function is executed
+ before each request, even if outside of a blueprint.
+ """
+ self.record_once(lambda s: s.app.before_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def before_app_first_request(self, f):
+ """Like :meth:`Flask.before_first_request`. Such a function is
+ executed before the first request to the application.
+ """
+ self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
+ return f
+
+ def after_request(self, f):
+ """Like :meth:`Flask.after_request` but for a blueprint. This function
+ is only executed after each request that is handled by a function of
+ that blueprint.
+ """
+ self.record_once(lambda s: s.app.after_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def after_app_request(self, f):
+ """Like :meth:`Flask.after_request` but for a blueprint. Such a function
+ is executed after each request, even if outside of the blueprint.
+ """
+ self.record_once(lambda s: s.app.after_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def teardown_request(self, f):
+ """Like :meth:`Flask.teardown_request` 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.
+ """
+ self.record_once(lambda s: s.app.teardown_request_funcs
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def teardown_app_request(self, f):
+ """Like :meth:`Flask.teardown_request` but for a blueprint. Such a
+ function is executed when tearing down each request, even if outside of
+ the blueprint.
+ """
+ self.record_once(lambda s: s.app.teardown_request_funcs
+ .setdefault(None, []).append(f))
+ return f
+
+ def context_processor(self, f):
+ """Like :meth:`Flask.context_processor` but for a blueprint. This
+ function is only executed for requests handled by a blueprint.
+ """
+ self.record_once(lambda s: s.app.template_context_processors
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def app_context_processor(self, f):
+ """Like :meth:`Flask.context_processor` but for a blueprint. Such a
+ function is executed each request, even if outside of the blueprint.
+ """
+ self.record_once(lambda s: s.app.template_context_processors
+ .setdefault(None, []).append(f))
+ return f
+
+ def app_errorhandler(self, code):
+ """Like :meth:`Flask.errorhandler` but for a blueprint. This
+ handler is used for all requests, even if outside of the blueprint.
+ """
+ def decorator(f):
+ self.record_once(lambda s: s.app.errorhandler(code)(f))
+ return f
+ return decorator
+
+ def url_value_preprocessor(self, f):
+ """Registers a function as URL value preprocessor for this
+ blueprint. It's called before the view functions are called and
+ can modify the url values provided.
+ """
+ self.record_once(lambda s: s.app.url_value_preprocessors
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def url_defaults(self, f):
+ """Callback function for URL defaults for this blueprint. It's called
+ with the endpoint and values and should update the values passed
+ in place.
+ """
+ self.record_once(lambda s: s.app.url_default_functions
+ .setdefault(self.name, []).append(f))
+ return f
+
+ def app_url_value_preprocessor(self, f):
+ """Same as :meth:`url_value_preprocessor` but application wide.
+ """
+ self.record_once(lambda s: s.app.url_value_preprocessors
+ .setdefault(None, []).append(f))
+ return f
+
+ def app_url_defaults(self, f):
+ """Same as :meth:`url_defaults` but application wide.
+ """
+ self.record_once(lambda s: s.app.url_default_functions
+ .setdefault(None, []).append(f))
+ return f
+
+ def errorhandler(self, code_or_exception):
+ """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.
+
+ Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator
+ of the :class:`~flask.Flask` object.
+ """
+ def decorator(f):
+ self.record_once(lambda s: s.app._register_error_handler(
+ self.name, code_or_exception, f))
+ return f
+ return decorator
diff --git a/websdk/flask/config.py b/websdk/flask/config.py
new file mode 100644
index 0000000..67dbf9b
--- /dev/null
+++ b/websdk/flask/config.py
@@ -0,0 +1,169 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.config
+ ~~~~~~~~~~~~
+
+ Implements the configuration related objects.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import imp
+import os
+import errno
+
+from werkzeug.utils import import_string
+
+
+class ConfigAttribute(object):
+ """Makes an attribute forward to the config"""
+
+ def __init__(self, name, get_converter=None):
+ self.__name__ = name
+ self.get_converter = get_converter
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ rv = obj.config[self.__name__]
+ if self.get_converter is not None:
+ rv = self.get_converter(rv)
+ return rv
+
+ def __set__(self, obj, value):
+ obj.config[self.__name__] = value
+
+
+class Config(dict):
+ """Works exactly like a dict but provides ways to fill it from files
+ or special dictionaries. There are two common patterns to populate the
+ config.
+
+ Either you can fill the config from a config file::
+
+ app.config.from_pyfile('yourconfig.cfg')
+
+ Or alternatively you can define the configuration options in the
+ module that calls :meth:`from_object` or provide an import path to
+ a module that should be loaded. It is also possible to tell it to
+ use the same module and with that provide the configuration values
+ just before the call::
+
+ DEBUG = True
+ SECRET_KEY = 'development key'
+ app.config.from_object(__name__)
+
+ In both cases (loading from any Python file or loading from modules),
+ only uppercase keys are added to the config. This makes it possible to use
+ lowercase values in the config file for temporary values that are not added
+ to the config or to define the config keys in the same file that implements
+ the application.
+
+ Probably the most interesting way to load configurations is from an
+ environment variable pointing to a file::
+
+ app.config.from_envvar('YOURAPPLICATION_SETTINGS')
+
+ In this case before launching the application you have to set this
+ environment variable to the file you want to use. On Linux and OS X
+ use the export statement::
+
+ export YOURAPPLICATION_SETTINGS='/path/to/config/file'
+
+ On windows use `set` instead.
+
+ :param root_path: path to which files are read relative from. When the
+ config object is created by the application, this is
+ the application's :attr:`~flask.Flask.root_path`.
+ :param defaults: an optional dictionary of default values
+ """
+
+ def __init__(self, root_path, defaults=None):
+ dict.__init__(self, defaults or {})
+ self.root_path = root_path
+
+ def from_envvar(self, variable_name, silent=False):
+ """Loads a configuration from an environment variable pointing to
+ a configuration file. This is basically just a shortcut with nicer
+ error messages for this line of code::
+
+ app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
+
+ :param variable_name: name of the environment variable
+ :param silent: set to `True` if you want silent failure for missing
+ files.
+ :return: bool. `True` if able to load config, `False` otherwise.
+ """
+ rv = os.environ.get(variable_name)
+ if not rv:
+ if silent:
+ return False
+ raise RuntimeError('The environment variable %r is not set '
+ 'and as such configuration could not be '
+ 'loaded. Set this variable and make it '
+ 'point to a configuration file' %
+ variable_name)
+ self.from_pyfile(rv)
+ return True
+
+ def from_pyfile(self, filename, silent=False):
+ """Updates the values in the config from a Python file. This function
+ behaves as if the file was imported as module with the
+ :meth:`from_object` function.
+
+ :param filename: the filename of the config. This can either be an
+ absolute filename or a filename relative to the
+ root path.
+ :param silent: set to `True` if you want silent failure for missing
+ files.
+
+ .. versionadded:: 0.7
+ `silent` parameter.
+ """
+ filename = os.path.join(self.root_path, filename)
+ d = imp.new_module('config')
+ d.__file__ = filename
+ try:
+ execfile(filename, d.__dict__)
+ except IOError, e:
+ if silent and e.errno in (errno.ENOENT, errno.EISDIR):
+ return False
+ e.strerror = 'Unable to load configuration file (%s)' % e.strerror
+ raise
+ self.from_object(d)
+ return True
+
+ def from_object(self, obj):
+ """Updates the values from the given object. An object can be of one
+ of the following two types:
+
+ - a string: in this case the object with that name will be imported
+ - an actual object reference: that object is used directly
+
+ Objects are usually either modules or classes.
+
+ Just the uppercase variables in that object are stored in the config.
+ Example usage::
+
+ app.config.from_object('yourapplication.default_config')
+ from yourapplication import default_config
+ app.config.from_object(default_config)
+
+ You should not use this function to load the actual configuration but
+ rather configuration defaults. The actual config should be loaded
+ with :meth:`from_pyfile` and ideally from a location not within the
+ package because the package might be installed system wide.
+
+ :param obj: an import name or object
+ """
+ if isinstance(obj, basestring):
+ obj = import_string(obj)
+ for key in dir(obj):
+ if key.isupper():
+ self[key] = getattr(obj, key)
+
+ def __repr__(self):
+ return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
diff --git a/websdk/flask/ctx.py b/websdk/flask/ctx.py
new file mode 100644
index 0000000..26781db
--- /dev/null
+++ b/websdk/flask/ctx.py
@@ -0,0 +1,175 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.ctx
+ ~~~~~~~~~
+
+ Implements the objects required to keep the context.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug.exceptions import HTTPException
+
+from .globals import _request_ctx_stack
+from .module import blueprint_is_module
+
+
+class _RequestGlobals(object):
+ pass
+
+
+def has_request_context():
+ """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's available but fail silently if the request
+ object is unavailable.
+
+ ::
+
+ class User(db.Model):
+
+ def __init__(self, username, remote_addr=None):
+ self.username = username
+ if remote_addr is None and has_request_context():
+ remote_addr = request.remote_addr
+ self.remote_addr = remote_addr
+
+ Alternatively you can also just test any of the context bound objects
+ (such as :class:`request` or :class:`g` for truthness)::
+
+ class User(db.Model):
+
+ def __init__(self, username, remote_addr=None):
+ self.username = username
+ if remote_addr is None and request:
+ remote_addr = request.remote_addr
+ self.remote_addr = remote_addr
+
+ .. versionadded:: 0.7
+ """
+ return _request_ctx_stack.top is not None
+
+
+class RequestContext(object):
+ """The request context contains all request relevant information. It is
+ created at the beginning of the request and pushed to the
+ `_request_ctx_stack` and removed at the end of it. It will create the
+ URL adapter and request object for the WSGI environment provided.
+
+ Do not attempt to use this class directly, instead use
+ :meth:`~flask.Flask.test_request_context` and
+ :meth:`~flask.Flask.request_context` to create this object.
+
+ When the request context is popped, it will evaluate all the
+ functions registered on the application for teardown execution
+ (:meth:`~flask.Flask.teardown_request`).
+
+ The request context is automatically popped at the end of the request
+ for you. In debug mode the request context is kept around if
+ exceptions happen so that interactive debuggers have a chance to
+ introspect the data. With 0.4 this can also be forced for requests
+ that did not fail and outside of `DEBUG` mode. By setting
+ ``'flask._preserve_context'`` to `True` on the WSGI environment the
+ context will not pop itself at the end of the request. This is used by
+ the :meth:`~flask.Flask.test_client` for example to implement the
+ deferred cleanup functionality.
+
+ You might find this helpful for unittests where you need the
+ information from the context local around for a little longer. Make
+ sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in
+ that situation, otherwise your unittests will leak memory.
+ """
+
+ def __init__(self, app, environ):
+ self.app = app
+ self.request = app.request_class(environ)
+ self.url_adapter = app.create_url_adapter(self.request)
+ self.g = _RequestGlobals()
+ self.flashes = None
+ self.session = None
+
+ # indicator if the context was preserved. Next time another context
+ # is pushed the preserved context is popped.
+ self.preserved = False
+
+ self.match_request()
+
+ # XXX: Support for deprecated functionality. This is doing away with
+ # Flask 1.0
+ blueprint = self.request.blueprint
+ if blueprint is not None:
+ # better safe than sorry, we don't want to break code that
+ # already worked
+ bp = app.blueprints.get(blueprint)
+ if bp is not None and blueprint_is_module(bp):
+ self.request._is_old_module = True
+
+ def match_request(self):
+ """Can be overridden by a subclass to hook into the matching
+ of the request.
+ """
+ try:
+ url_rule, self.request.view_args = \
+ self.url_adapter.match(return_rule=True)
+ self.request.url_rule = url_rule
+ except HTTPException, e:
+ self.request.routing_exception = e
+
+ def push(self):
+ """Binds the request context to the current context."""
+ # If an exception ocurrs in debug mode or if context preservation is
+ # activated under exception situations exactly one context stays
+ # on the stack. The rationale is that you want to access that
+ # information under debug situations. However if someone forgets to
+ # pop that context again we want to make sure that on the next push
+ # it's invalidated otherwise we run at risk that something leaks
+ # memory. This is usually only a problem in testsuite since this
+ # functionality is not active in production environments.
+ top = _request_ctx_stack.top
+ if top is not None and top.preserved:
+ top.pop()
+
+ _request_ctx_stack.push(self)
+
+ # Open the session at the moment that the request context is
+ # available. This allows a custom open_session method to use the
+ # request context (e.g. flask-sqlalchemy).
+ self.session = self.app.open_session(self.request)
+ if self.session is None:
+ self.session = self.app.make_null_session()
+
+ def pop(self):
+ """Pops the request context and unbinds it by doing that. This will
+ also trigger the execution of functions registered by the
+ :meth:`~flask.Flask.teardown_request` decorator.
+ """
+ self.preserved = False
+ self.app.do_teardown_request()
+ rv = _request_ctx_stack.pop()
+ assert rv is self, 'Popped wrong request context. (%r instead of %r)' \
+ % (rv, self)
+
+ def __enter__(self):
+ self.push()
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ # do not pop the request stack if we are in debug mode and an
+ # exception happened. This will allow the debugger to still
+ # access the request object in the interactive shell. Furthermore
+ # the context can be force kept alive for the test client.
+ # See flask.testing for how this works.
+ if self.request.environ.get('flask._preserve_context') or \
+ (tb is not None and self.app.preserve_context_on_exception):
+ self.preserved = True
+ else:
+ self.pop()
+
+ def __repr__(self):
+ return '<%s \'%s\' [%s] of %s>' % (
+ self.__class__.__name__,
+ self.request.url,
+ self.request.method,
+ self.app.name
+ )
diff --git a/websdk/flask/debughelpers.py b/websdk/flask/debughelpers.py
new file mode 100644
index 0000000..edf8c11
--- /dev/null
+++ b/websdk/flask/debughelpers.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.debughelpers
+ ~~~~~~~~~~~~~~~~~~
+
+ Various helpers to make the development experience better.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+
+class DebugFilesKeyError(KeyError, AssertionError):
+ """Raised from request.files during debugging. The idea is that it can
+ provide a better error message than just a generic KeyError/BadRequest.
+ """
+
+ def __init__(self, request, key):
+ form_matches = request.form.getlist(key)
+ buf = ['You tried to access the file "%s" in the request.files '
+ 'dictionary but it does not exist. The mimetype for the request '
+ 'is "%s" instead of "multipart/form-data" which means that no '
+ 'file contents were transmitted. To fix this error you should '
+ 'provide enctype="multipart/form-data" in your form.' %
+ (key, request.mimetype)]
+ if form_matches:
+ buf.append('\n\nThe browser instead transmitted some file names. '
+ 'This was submitted: %s' % ', '.join('"%s"' % x
+ for x in form_matches))
+ self.msg = ''.join(buf).encode('utf-8')
+
+ def __str__(self):
+ return self.msg
+
+
+class FormDataRoutingRedirect(AssertionError):
+ """This exception is raised by Flask in debug mode if it detects a
+ redirect caused by the routing system when the request method is not
+ GET, HEAD or OPTIONS. Reasoning: form data will be dropped.
+ """
+
+ def __init__(self, request):
+ exc = request.routing_exception
+ buf = ['A request was sent to this URL (%s) but a redirect was '
+ 'issued automatically by the routing system to "%s".'
+ % (request.url, exc.new_url)]
+
+ # In case just a slash was appended we can be extra helpful
+ if request.base_url + '/' == exc.new_url.split('?')[0]:
+ buf.append(' The URL was defined with a trailing slash so '
+ 'Flask will automatically redirect to the URL '
+ 'with the trailing slash if it was accessed '
+ 'without one.')
+
+ buf.append(' Make sure to directly send your %s-request to this URL '
+ 'since we can\'t make browsers or HTTP clients redirect '
+ 'with form data reliably or without user interaction.' %
+ request.method)
+ buf.append('\n\nNote: this exception is only raised in debug mode')
+ AssertionError.__init__(self, ''.join(buf).encode('utf-8'))
+
+
+def attach_enctype_error_multidict(request):
+ """Since Flask 0.8 we're monkeypatching the files object in case a
+ request is detected that does not use multipart form data but the files
+ object is accessed.
+ """
+ oldcls = request.files.__class__
+ class newcls(oldcls):
+ def __getitem__(self, key):
+ try:
+ return oldcls.__getitem__(self, key)
+ except KeyError, e:
+ if key not in request.form:
+ raise
+ raise DebugFilesKeyError(request, key)
+ newcls.__name__ = oldcls.__name__
+ newcls.__module__ = oldcls.__module__
+ request.files.__class__ = newcls
diff --git a/websdk/flask/ext/__init__.py b/websdk/flask/ext/__init__.py
new file mode 100644
index 0000000..f29958a
--- /dev/null
+++ b/websdk/flask/ext/__init__.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.ext
+ ~~~~~~~~~
+
+ Redirect imports for extensions. This module basically makes it possible
+ for us to transition from flaskext.foo to flask_foo without having to
+ force all extensions to upgrade at the same time.
+
+ When a user does ``from flask.ext.foo import bar`` it will attempt to
+ import ``from flask_foo import bar`` first and when that fails it will
+ try to import ``from flaskext.foo import bar``.
+
+ We're switching from namespace packages because it was just too painful for
+ everybody involved.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+
+def setup():
+ from ..exthook import ExtensionImporter
+ importer = ExtensionImporter(['flask_%s', 'flaskext.%s'], __name__)
+ importer.install()
+
+
+setup()
+del setup
diff --git a/websdk/flask/exthook.py b/websdk/flask/exthook.py
new file mode 100644
index 0000000..bb1deb2
--- /dev/null
+++ b/websdk/flask/exthook.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.exthook
+ ~~~~~~~~~~~~~
+
+ Redirect imports for extensions. This module basically makes it possible
+ for us to transition from flaskext.foo to flask_foo without having to
+ force all extensions to upgrade at the same time.
+
+ When a user does ``from flask.ext.foo import bar`` it will attempt to
+ import ``from flask_foo import bar`` first and when that fails it will
+ try to import ``from flaskext.foo import bar``.
+
+ We're switching from namespace packages because it was just too painful for
+ everybody involved.
+
+ This is used by `flask.ext`.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+import os
+
+
+class ExtensionImporter(object):
+ """This importer redirects imports from this submodule to other locations.
+ This makes it possible to transition from the old flaskext.name to the
+ newer flask_name without people having a hard time.
+ """
+
+ def __init__(self, module_choices, wrapper_module):
+ self.module_choices = module_choices
+ self.wrapper_module = wrapper_module
+ self.prefix = wrapper_module + '.'
+ self.prefix_cutoff = wrapper_module.count('.') + 1
+
+ def __eq__(self, other):
+ return self.__class__.__module__ == other.__class__.__module__ and \
+ self.__class__.__name__ == other.__class__.__name__ and \
+ self.wrapper_module == other.wrapper_module and \
+ self.module_choices == other.module_choices
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def install(self):
+ sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self]
+
+ def find_module(self, fullname, path=None):
+ if fullname.startswith(self.prefix):
+ return self
+
+ def load_module(self, fullname):
+ if fullname in sys.modules:
+ return sys.modules[fullname]
+ modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff]
+ for path in self.module_choices:
+ realname = path % modname
+ try:
+ __import__(realname)
+ except ImportError:
+ exc_type, exc_value, tb = sys.exc_info()
+ # since we only establish the entry in sys.modules at the
+ # very this seems to be redundant, but if recursive imports
+ # happen we will call into the move import a second time.
+ # On the second invocation we still don't have an entry for
+ # fullname in sys.modules, but we will end up with the same
+ # fake module name and that import will succeed since this
+ # one already has a temporary entry in the modules dict.
+ # Since this one "succeeded" temporarily that second
+ # invocation now will have created a fullname entry in
+ # sys.modules which we have to kill.
+ sys.modules.pop(fullname, None)
+
+ # If it's an important traceback we reraise it, otherwise
+ # we swallow it and try the next choice. The skipped frame
+ # is the one from __import__ above which we don't care about
+ if self.is_important_traceback(realname, tb):
+ raise exc_type, exc_value, tb.tb_next
+ continue
+ module = sys.modules[fullname] = sys.modules[realname]
+ if '.' not in modname:
+ setattr(sys.modules[self.wrapper_module], modname, module)
+ return module
+ raise ImportError('No module named %s' % fullname)
+
+ def is_important_traceback(self, important_module, tb):
+ """Walks a traceback's frames and checks if any of the frames
+ originated in the given important module. If that is the case then we
+ were able to import the module itself but apparently something went
+ wrong when the module was imported. (Eg: import of an import failed).
+ """
+ while tb is not None:
+ if self.is_important_frame(important_module, tb):
+ return True
+ tb = tb.tb_next
+ return False
+
+ def is_important_frame(self, important_module, tb):
+ """Checks a single frame if it's important."""
+ g = tb.tb_frame.f_globals
+ if '__name__' not in g:
+ return False
+
+ module_name = g['__name__']
+
+ # Python 2.7 Behavior. Modules are cleaned up late so the
+ # name shows up properly here. Success!
+ if module_name == important_module:
+ return True
+
+ # Some python verisons will will clean up modules so early that the
+ # module name at that point is no longer set. Try guessing from
+ # the filename then.
+ filename = os.path.abspath(tb.tb_frame.f_code.co_filename)
+ test_string = os.path.sep + important_module.replace('.', os.path.sep)
+ return test_string + '.py' in filename or \
+ test_string + os.path.sep + '__init__.py' in filename
diff --git a/websdk/flask/globals.py b/websdk/flask/globals.py
new file mode 100644
index 0000000..16580d1
--- /dev/null
+++ b/websdk/flask/globals.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.globals
+ ~~~~~~~~~~~~~
+
+ Defines all the global objects that are proxies to the current
+ active context.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from functools import partial
+from werkzeug.local import LocalStack, LocalProxy
+
+def _lookup_object(name):
+ top = _request_ctx_stack.top
+ if top is None:
+ raise RuntimeError('working outside of request context')
+ return getattr(top, name)
+
+
+# context locals
+_request_ctx_stack = LocalStack()
+current_app = LocalProxy(partial(_lookup_object, 'app'))
+request = LocalProxy(partial(_lookup_object, 'request'))
+session = LocalProxy(partial(_lookup_object, 'session'))
+g = LocalProxy(partial(_lookup_object, 'g'))
diff --git a/websdk/flask/helpers.py b/websdk/flask/helpers.py
new file mode 100644
index 0000000..72c8f17
--- /dev/null
+++ b/websdk/flask/helpers.py
@@ -0,0 +1,649 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.helpers
+ ~~~~~~~~~~~~~
+
+ Implements various helpers.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import os
+import sys
+import posixpath
+import mimetypes
+from time import time
+from zlib import adler32
+from threading import RLock
+
+# try to load the best simplejson implementation available. If JSON
+# is not installed, we add a failing class.
+json_available = True
+json = None
+try:
+ import simplejson as json
+except ImportError:
+ try:
+ import json
+ except ImportError:
+ try:
+ # Google Appengine offers simplejson via django
+ from django.utils import simplejson as json
+ except ImportError:
+ json_available = False
+
+
+from werkzeug.datastructures import Headers
+from werkzeug.exceptions import NotFound
+
+# this was moved in 0.7
+try:
+ from werkzeug.wsgi import wrap_file
+except ImportError:
+ from werkzeug.utils import wrap_file
+
+from jinja2 import FileSystemLoader
+
+from .globals import session, _request_ctx_stack, current_app, request
+
+
+def _assert_have_json():
+ """Helper function that fails if JSON is unavailable."""
+ if not json_available:
+ raise RuntimeError('simplejson not installed')
+
+# figure out if simplejson escapes slashes. This behaviour was changed
+# from one version to another without reason.
+if not json_available or '\\/' not in json.dumps('/'):
+
+ def _tojson_filter(*args, **kwargs):
+ if __debug__:
+ _assert_have_json()
+ return json.dumps(*args, **kwargs).replace('/', '\\/')
+else:
+ _tojson_filter = json.dumps
+
+
+# sentinel
+_missing = object()
+
+
+# what separators does this operating system provide that are not a slash?
+# this is used by the send_from_directory function to ensure that nobody is
+# able to access files from outside the filesystem.
+_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
+ if sep not in (None, '/'))
+
+
+def _endpoint_from_view_func(view_func):
+ """Internal helper that returns the default endpoint for a given
+ function. This always is the function name.
+ """
+ assert view_func is not None, 'expected view func if endpoint ' \
+ 'is not provided.'
+ return view_func.__name__
+
+
+def jsonify(*args, **kwargs):
+ """Creates a :class:`~flask.Response` with the JSON representation of
+ the given arguments with an `application/json` mimetype. The arguments
+ to this function are the same as to the :class:`dict` constructor.
+
+ Example usage::
+
+ @app.route('/_get_current_user')
+ def get_current_user():
+ return jsonify(username=g.user.username,
+ email=g.user.email,
+ id=g.user.id)
+
+ This will send a JSON response like this to the browser::
+
+ {
+ "username": "admin",
+ "email": "admin@localhost",
+ "id": 42
+ }
+
+ This requires Python 2.6 or an installed version of simplejson. For
+ security reasons only objects are supported toplevel. For more
+ information about this, have a look at :ref:`json-security`.
+
+ .. versionadded:: 0.2
+ """
+ if __debug__:
+ _assert_have_json()
+ return current_app.response_class(json.dumps(dict(*args, **kwargs),
+ indent=None if request.is_xhr else 2), mimetype='application/json')
+
+
+def make_response(*args):
+ """Sometimes it is necessary to set additional headers in a view. Because
+ views do not have to return response objects but can return a value that
+ is converted into a response object by Flask itself, it becomes tricky to
+ add headers to it. This function can be called instead of using a return
+ and you will get a response object which you can use to attach headers.
+
+ If view looked like this and you want to add a new header::
+
+ def index():
+ return render_template('index.html', foo=42)
+
+ You can now do something like this::
+
+ def index():
+ response = make_response(render_template('index.html', foo=42))
+ response.headers['X-Parachutes'] = 'parachutes are cool'
+ return response
+
+ This function accepts the very same arguments you can return from a
+ view function. This for example creates a response with a 404 error
+ code::
+
+ response = make_response(render_template('not_found.html'), 404)
+
+ 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::
+
+ response = make_response(view_function())
+ response.headers['X-Parachutes'] = 'parachutes are cool'
+
+ Internally this function does the following things:
+
+ - if no arguments are passed, it creates a new response argument
+ - if one argument is passed, :meth:`flask.Flask.make_response`
+ is invoked with it.
+ - if more than one argument is passed, the arguments are passed
+ to the :meth:`flask.Flask.make_response` function as tuple.
+
+ .. versionadded:: 0.6
+ """
+ if not args:
+ return current_app.response_class()
+ if len(args) == 1:
+ args = args[0]
+ return current_app.make_response(args)
+
+
+def url_for(endpoint, **values):
+ """Generates a URL to the given endpoint with the method provided.
+
+ 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 `None`, 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 (``.``).
+
+ This will reference the index function local to the current blueprint::
+
+ url_for('.index')
+
+ For more information, head over to the :ref:`Quickstart <url-building>`.
+
+ :param endpoint: the endpoint of the URL (name of the function)
+ :param values: the variable arguments of the URL rule
+ :param _external: if set to `True`, an absolute URL is generated.
+ """
+ ctx = _request_ctx_stack.top
+ blueprint_name = request.blueprint
+ if not ctx.request._is_old_module:
+ if endpoint[:1] == '.':
+ if blueprint_name is not None:
+ endpoint = blueprint_name + endpoint
+ else:
+ endpoint = endpoint[1:]
+ else:
+ # TODO: get rid of this deprecated functionality in 1.0
+ if '.' not in endpoint:
+ if blueprint_name is not None:
+ endpoint = blueprint_name + '.' + endpoint
+ elif endpoint.startswith('.'):
+ endpoint = endpoint[1:]
+ external = values.pop('_external', False)
+ ctx.app.inject_url_defaults(endpoint, values)
+ return ctx.url_adapter.build(endpoint, values, force_external=external)
+
+
+def get_template_attribute(template_name, attribute):
+ """Loads a macro (or variable) a template exports. This can be used to
+ invoke a macro from within Python code. If you for example have a
+ template named `_cider.html` with the following contents:
+
+ .. sourcecode:: html+jinja
+
+ {% macro hello(name) %}Hello {{ name }}!{% endmacro %}
+
+ You can access this from Python code like this::
+
+ hello = get_template_attribute('_cider.html', 'hello')
+ return hello('World')
+
+ .. versionadded:: 0.2
+
+ :param template_name: the name of the template
+ :param attribute: the name of the variable of macro to acccess
+ """
+ return getattr(current_app.jinja_env.get_template(template_name).module,
+ attribute)
+
+
+def flash(message, category='message'):
+ """Flashes a message to the next request. In order to remove the
+ flashed message from the session and to display it to the user,
+ the template has to call :func:`get_flashed_messages`.
+
+ .. versionchanged: 0.3
+ `category` parameter added.
+
+ :param message: the message to be flashed.
+ :param category: the category for the message. The following values
+ are recommended: ``'message'`` for any kind of message,
+ ``'error'`` for errors, ``'info'`` for information
+ messages and ``'warning'`` for warnings. However any
+ kind of string can be used as category.
+ """
+ session.setdefault('_flashes', []).append((category, message))
+
+
+def get_flashed_messages(with_categories=False):
+ """Pulls all flashed messages from the session and returns them.
+ Further calls in the same request to the function will return
+ the same messages. By default just the messages are returned,
+ but when `with_categories` is set to `True`, the return value will
+ be a list of tuples in the form ``(category, message)`` instead.
+
+ Example usage:
+
+ .. sourcecode:: html+jinja
+
+ {% for category, msg in get_flashed_messages(with_categories=true) %}
+ <p class=flash-{{ category }}>{{ msg }}
+ {% endfor %}
+
+ .. versionchanged:: 0.3
+ `with_categories` parameter added.
+
+ :param with_categories: set to `True` to also receive categories.
+ """
+ flashes = _request_ctx_stack.top.flashes
+ if flashes is None:
+ _request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \
+ if '_flashes' in session else []
+ if not with_categories:
+ return [x[1] for x in flashes]
+ return flashes
+
+
+def send_file(filename_or_fp, mimetype=None, as_attachment=False,
+ attachment_filename=None, add_etags=True,
+ cache_timeout=60 * 60 * 12, conditional=False):
+ """Sends the contents of a file to the client. This will use the
+ most efficient method available and configured. By default it will
+ try to use the WSGI server's file_wrapper support. Alternatively
+ you can set the application's :attr:`~Flask.use_x_sendfile` attribute
+ to ``True`` to directly emit an `X-Sendfile` header. This however
+ requires support of the underlying webserver for `X-Sendfile`.
+
+ By default it will try to guess the mimetype for you, but you can
+ also explicitly provide one. For extra security you probably want
+ to send certain files as attachment (HTML for instance). The mimetype
+ guessing requires a `filename` or an `attachment_filename` to be
+ provided.
+
+ Please never pass filenames to this function from user sources without
+ checking them first. Something like this is usually sufficient to
+ avoid security problems::
+
+ if '..' in filename or filename.startswith('/'):
+ abort(404)
+
+ .. versionadded:: 0.2
+
+ .. versionadded:: 0.5
+ The `add_etags`, `cache_timeout` and `conditional` parameters were
+ added. The default behaviour is now to attach etags.
+
+ .. versionchanged:: 0.7
+ mimetype guessing and etag support for file objects was
+ deprecated because it was unreliable. Pass a filename if you are
+ able to, otherwise attach an etag yourself. This functionality
+ will be removed in Flask 1.0
+
+ :param filename_or_fp: the filename of the file to send. This is
+ relative to the :attr:`~Flask.root_path` if a
+ relative path is specified.
+ Alternatively a file object might be provided
+ in which case `X-Sendfile` might not work and
+ fall back to the traditional method. Make sure
+ that the file pointer is positioned at the start
+ of data to send before calling :func:`send_file`.
+ :param mimetype: the mimetype of the file if provided, otherwise
+ auto detection happens.
+ :param as_attachment: set to `True` if you want to send this file with
+ a ``Content-Disposition: attachment`` header.
+ :param attachment_filename: the filename for the attachment if it
+ differs from the file's filename.
+ :param add_etags: set to `False` to disable attaching of etags.
+ :param conditional: set to `True` to enable conditional responses.
+ :param cache_timeout: the timeout in seconds for the headers.
+ """
+ mtime = None
+ if isinstance(filename_or_fp, basestring):
+ filename = filename_or_fp
+ file = None
+ else:
+ from warnings import warn
+ file = filename_or_fp
+ filename = getattr(file, 'name', None)
+
+ # XXX: this behaviour is now deprecated because it was unreliable.
+ # removed in Flask 1.0
+ if not attachment_filename and not mimetype \
+ and isinstance(filename, basestring):
+ warn(DeprecationWarning('The filename support for file objects '
+ 'passed to send_file is now deprecated. Pass an '
+ 'attach_filename if you want mimetypes to be guessed.'),
+ stacklevel=2)
+ if add_etags:
+ warn(DeprecationWarning('In future flask releases etags will no '
+ 'longer be generated for file objects passed to the send_file '
+ 'function because this behaviour was unreliable. Pass '
+ 'filenames instead if possible, otherwise attach an etag '
+ 'yourself based on another value'), stacklevel=2)
+
+ if filename is not None:
+ if not os.path.isabs(filename):
+ filename = os.path.join(current_app.root_path, filename)
+ if mimetype is None and (filename or attachment_filename):
+ mimetype = mimetypes.guess_type(filename or attachment_filename)[0]
+ if mimetype is None:
+ mimetype = 'application/octet-stream'
+
+ headers = Headers()
+ if as_attachment:
+ if attachment_filename is None:
+ if filename is None:
+ raise TypeError('filename unavailable, required for '
+ 'sending as attachment')
+ attachment_filename = os.path.basename(filename)
+ headers.add('Content-Disposition', 'attachment',
+ filename=attachment_filename)
+
+ if current_app.use_x_sendfile and filename:
+ if file is not None:
+ file.close()
+ headers['X-Sendfile'] = filename
+ data = None
+ else:
+ if file is None:
+ file = open(filename, 'rb')
+ mtime = os.path.getmtime(filename)
+ data = wrap_file(request.environ, file)
+
+ rv = current_app.response_class(data, mimetype=mimetype, headers=headers,
+ direct_passthrough=True)
+
+ # if we know the file modification date, we can store it as the
+ # the time of the last modification.
+ if mtime is not None:
+ rv.last_modified = int(mtime)
+
+ rv.cache_control.public = True
+ if cache_timeout:
+ rv.cache_control.max_age = cache_timeout
+ rv.expires = int(time() + cache_timeout)
+
+ if add_etags and filename is not None:
+ rv.set_etag('flask-%s-%s-%s' % (
+ os.path.getmtime(filename),
+ os.path.getsize(filename),
+ adler32(
+ filename.encode('utf8') if isinstance(filename, unicode)
+ else filename
+ ) & 0xffffffff
+ ))
+ if conditional:
+ rv = rv.make_conditional(request)
+ # make sure we don't send x-sendfile for servers that
+ # ignore the 304 status code for x-sendfile.
+ if rv.status_code == 304:
+ rv.headers.pop('x-sendfile', None)
+ return rv
+
+
+def safe_join(directory, filename):
+ """Safely join `directory` and `filename`.
+
+ Example usage::
+
+ @app.route('/wiki/<path:filename>')
+ def wiki_page(filename):
+ filename = safe_join(app.config['WIKI_FOLDER'], filename)
+ with open(filename, 'rb') as fd:
+ content = fd.read() # Read and process the file content...
+
+ :param directory: the base directory.
+ :param filename: the untrusted filename relative to that directory.
+ :raises: :class:`~werkzeug.exceptions.NotFound` if the resulting path
+ would fall out of `directory`.
+ """
+ filename = posixpath.normpath(filename)
+ for sep in _os_alt_seps:
+ if sep in filename:
+ raise NotFound()
+ if os.path.isabs(filename) or filename.startswith('../'):
+ raise NotFound()
+ return os.path.join(directory, filename)
+
+
+def send_from_directory(directory, filename, **options):
+ """Send a file from a given directory with :func:`send_file`. This
+ is a secure way to quickly expose static files from an upload folder
+ or something similar.
+
+ Example usage::
+
+ @app.route('/uploads/<path:filename>')
+ def download_file(filename):
+ return send_from_directory(app.config['UPLOAD_FOLDER'],
+ filename, as_attachment=True)
+
+ .. admonition:: Sending files and Performance
+
+ It is strongly recommended to activate either `X-Sendfile` support in
+ your webserver or (if no authentication happens) to tell the webserver
+ to serve files for the given path on its own without calling into the
+ web application for improved performance.
+
+ .. versionadded:: 0.5
+
+ :param directory: the directory where all the files are stored.
+ :param filename: the filename relative to that directory to
+ download.
+ :param options: optional keyword arguments that are directly
+ forwarded to :func:`send_file`.
+ """
+ filename = safe_join(directory, filename)
+ if not os.path.isfile(filename):
+ raise NotFound()
+ return send_file(filename, conditional=True, **options)
+
+
+def get_root_path(import_name):
+ """Returns the path to a package or cwd if that cannot be found. This
+ returns the path of a package or the folder that contains a module.
+
+ Not to be confused with the package path returned by :func:`find_package`.
+ """
+ __import__(import_name)
+ try:
+ directory = os.path.dirname(sys.modules[import_name].__file__)
+ return os.path.abspath(directory)
+ except AttributeError:
+ # this is necessary in case we are running from the interactive
+ # python shell. It will never be used for production code however
+ return os.getcwd()
+
+
+def find_package(import_name):
+ """Finds a package and returns the prefix (or None if the package is
+ not installed) as well as the folder that contains the package or
+ module as a tuple. The package path returned is the module that would
+ have to be added to the pythonpath in order to make it possible to
+ import the module. The prefix is the path below which a UNIX like
+ folder structure exists (lib, share etc.).
+ """
+ __import__(import_name)
+ root_mod = sys.modules[import_name.split('.')[0]]
+ package_path = getattr(root_mod, '__file__', None)
+ if package_path is None:
+ # support for the interactive python shell
+ package_path = os.getcwd()
+ else:
+ package_path = os.path.abspath(os.path.dirname(package_path))
+ if hasattr(root_mod, '__path__'):
+ package_path = os.path.dirname(package_path)
+
+ # leave the egg wrapper folder or the actual .egg on the filesystem
+ test_package_path = package_path
+ if os.path.basename(test_package_path).endswith('.egg'):
+ test_package_path = os.path.dirname(test_package_path)
+
+ site_parent, site_folder = os.path.split(test_package_path)
+ py_prefix = os.path.abspath(sys.prefix)
+ if test_package_path.startswith(py_prefix):
+ return py_prefix, package_path
+ elif site_folder.lower() == 'site-packages':
+ parent, folder = os.path.split(site_parent)
+ # Windows like installations
+ if folder.lower() == 'lib':
+ base_dir = parent
+ # UNIX like installations
+ elif os.path.basename(parent).lower() == 'lib':
+ base_dir = os.path.dirname(parent)
+ else:
+ base_dir = site_parent
+ return base_dir, package_path
+ return None, package_path
+
+
+class locked_cached_property(object):
+ """A decorator that converts a function into a lazy property. The
+ function wrapped is called the first time to retrieve the result
+ and then that calculated result is used the next time you access
+ the value. Works like the one in Werkzeug but has a lock for
+ thread safety.
+ """
+
+ def __init__(self, func, name=None, doc=None):
+ self.__name__ = name or func.__name__
+ self.__module__ = func.__module__
+ self.__doc__ = doc or func.__doc__
+ self.func = func
+ self.lock = RLock()
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ with self.lock:
+ value = obj.__dict__.get(self.__name__, _missing)
+ if value is _missing:
+ value = self.func(obj)
+ obj.__dict__[self.__name__] = value
+ return value
+
+
+class _PackageBoundObject(object):
+
+ def __init__(self, import_name, template_folder=None):
+ #: The name of the package or module. Do not change this once
+ #: it was set by the constructor.
+ self.import_name = import_name
+
+ #: location of the templates. `None` if templates should not be
+ #: exposed.
+ self.template_folder = template_folder
+
+ #: Where is the app root located?
+ self.root_path = get_root_path(self.import_name)
+
+ self._static_folder = None
+ self._static_url_path = None
+
+ def _get_static_folder(self):
+ if self._static_folder is not None:
+ return os.path.join(self.root_path, self._static_folder)
+ def _set_static_folder(self, value):
+ self._static_folder = value
+ static_folder = property(_get_static_folder, _set_static_folder)
+ del _get_static_folder, _set_static_folder
+
+ def _get_static_url_path(self):
+ if self._static_url_path is None:
+ if self.static_folder is None:
+ return None
+ return '/' + os.path.basename(self.static_folder)
+ return self._static_url_path
+ def _set_static_url_path(self, value):
+ self._static_url_path = value
+ static_url_path = property(_get_static_url_path, _set_static_url_path)
+ del _get_static_url_path, _set_static_url_path
+
+ @property
+ def has_static_folder(self):
+ """This is `True` if the package bound object's container has a
+ folder named ``'static'``.
+
+ .. versionadded:: 0.5
+ """
+ return self.static_folder is not None
+
+ @locked_cached_property
+ def jinja_loader(self):
+ """The Jinja loader for this package bound object.
+
+ .. versionadded:: 0.5
+ """
+ if self.template_folder is not None:
+ return FileSystemLoader(os.path.join(self.root_path,
+ self.template_folder))
+
+ def send_static_file(self, filename):
+ """Function used internally to send static files from the static
+ folder to the browser.
+
+ .. versionadded:: 0.5
+ """
+ if not self.has_static_folder:
+ raise RuntimeError('No static folder for this object')
+ return send_from_directory(self.static_folder, filename)
+
+ def open_resource(self, resource, mode='rb'):
+ """Opens a resource from the application's resource folder. To see
+ how this works, consider the following folder structure::
+
+ /myapplication.py
+ /schema.sql
+ /static
+ /style.css
+ /templates
+ /layout.html
+ /index.html
+
+ If you want to open the `schema.sql` file you would do the
+ following::
+
+ with app.open_resource('schema.sql') as f:
+ contents = f.read()
+ do_something_with(contents)
+
+ :param resource: the name of the resource. To access resources within
+ subfolders use forward slashes as separator.
+ """
+ if mode not in ('r', 'rb'):
+ raise ValueError('Resources can only be opened for reading')
+ return open(os.path.join(self.root_path, resource), mode)
diff --git a/websdk/flask/logging.py b/websdk/flask/logging.py
new file mode 100644
index 0000000..b992aef
--- /dev/null
+++ b/websdk/flask/logging.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.logging
+ ~~~~~~~~~~~~~
+
+ Implements the logging support for Flask.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import absolute_import
+
+from logging import getLogger, StreamHandler, Formatter, getLoggerClass, DEBUG
+
+
+def create_logger(app):
+ """Creates a logger for the given application. This logger works
+ similar to a regular Python logger but changes the effective logging
+ level based on the application's debug flag. Furthermore this
+ function also removes all attached handlers in case there was a
+ logger with the log name before.
+ """
+ Logger = getLoggerClass()
+
+ class DebugLogger(Logger):
+ def getEffectiveLevel(x):
+ return DEBUG if app.debug else Logger.getEffectiveLevel(x)
+
+ class DebugHandler(StreamHandler):
+ def emit(x, record):
+ StreamHandler.emit(x, record) if app.debug else None
+
+ handler = DebugHandler()
+ handler.setLevel(DEBUG)
+ handler.setFormatter(Formatter(app.debug_log_format))
+ logger = getLogger(app.logger_name)
+ # just in case that was not a new logger, get rid of all the handlers
+ # already attached to it.
+ del logger.handlers[:]
+ logger.__class__ = DebugLogger
+ logger.addHandler(handler)
+ return logger
diff --git a/websdk/flask/module.py b/websdk/flask/module.py
new file mode 100644
index 0000000..1c4f466
--- /dev/null
+++ b/websdk/flask/module.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.module
+ ~~~~~~~~~~~~
+
+ Implements a class that represents module blueprints.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import os
+
+from .blueprints import Blueprint
+
+
+def blueprint_is_module(bp):
+ """Used to figure out if something is actually a module"""
+ return isinstance(bp, Module)
+
+
+class Module(Blueprint):
+ """Deprecated module support. Until Flask 0.6 modules were a different
+ name of the concept now available as blueprints in Flask. They are
+ essentially doing the same but have some bad semantics for templates and
+ static files that were fixed with blueprints.
+
+ .. versionchanged:: 0.7
+ Modules were deprecated in favor for blueprints.
+ """
+
+ def __init__(self, import_name, name=None, url_prefix=None,
+ static_path=None, subdomain=None):
+ if name is None:
+ assert '.' in import_name, 'name required if package name ' \
+ 'does not point to a submodule'
+ name = import_name.rsplit('.', 1)[1]
+ Blueprint.__init__(self, name, import_name, url_prefix=url_prefix,
+ subdomain=subdomain, template_folder='templates')
+
+ if os.path.isdir(os.path.join(self.root_path, 'static')):
+ self._static_folder = 'static'
diff --git a/websdk/flask/session.py b/websdk/flask/session.py
new file mode 100644
index 0000000..4d4d2cd
--- /dev/null
+++ b/websdk/flask/session.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.session
+ ~~~~~~~~~~~~~
+
+ This module used to flask with the session global so we moved it
+ over to flask.sessions
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from warnings import warn
+warn(DeprecationWarning('please use flask.sessions instead'))
+
+from .sessions import *
+
+Session = SecureCookieSession
+_NullSession = NullSession
diff --git a/websdk/flask/sessions.py b/websdk/flask/sessions.py
new file mode 100644
index 0000000..2795bb1
--- /dev/null
+++ b/websdk/flask/sessions.py
@@ -0,0 +1,205 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.sessions
+ ~~~~~~~~~~~~~~
+
+ Implements cookie based sessions based on Werkzeug's secure cookie
+ system.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from datetime import datetime
+from werkzeug.contrib.securecookie import SecureCookie
+
+
+class SessionMixin(object):
+ """Expands a basic dictionary with an accessors that are expected
+ by Flask extensions and users for the session.
+ """
+
+ def _get_permanent(self):
+ return self.get('_permanent', False)
+
+ def _set_permanent(self, value):
+ self['_permanent'] = bool(value)
+
+ #: this reflects the ``'_permanent'`` key in the dict.
+ permanent = property(_get_permanent, _set_permanent)
+ del _get_permanent, _set_permanent
+
+ #: 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 `False` in.
+ new = False
+
+ #: for some backends this will always be `True`, 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 `True` in.
+ modified = True
+
+
+class SecureCookieSession(SecureCookie, SessionMixin):
+ """Expands the session with support for switching between permanent
+ and non-permanent sessions.
+ """
+
+
+class NullSession(SecureCookieSession):
+ """Class used to generate nicer error messages if sessions are not
+ available. Will still allow read-only access to the empty session
+ but fail on setting.
+ """
+
+ def _fail(self, *args, **kwargs):
+ raise RuntimeError('the session is unavailable because no secret '
+ 'key was set. Set the secret_key on the '
+ 'application to something unique and secret.')
+ __setitem__ = __delitem__ = clear = pop = popitem = \
+ update = setdefault = _fail
+ del _fail
+
+
+class SessionInterface(object):
+ """The basic interface you have to implement in order to replace the
+ default session interface which uses werkzeug's securecookie
+ implementation. The only methods you have to implement are
+ :meth:`open_session` and :meth:`save_session`, the others have
+ useful defaults which you don't need to change.
+
+ The session object returned by the :meth:`open_session` method has to
+ provide a dictionary like interface plus the properties and methods
+ from the :class:`SessionMixin`. We recommend just subclassing a dict
+ and adding that mixin::
+
+ class Session(dict, SessionMixin):
+ pass
+
+ If :meth:`open_session` returns `None` Flask will call into
+ :meth:`make_null_session` to create a session that acts as replacement
+ if the session support cannot work because some requirement is not
+ fulfilled. The default :class:`NullSession` class that is created
+ will complain that the secret key was not set.
+
+ To replace the session interface on an application all you have to do
+ is to assign :attr:`flask.Flask.session_interface`::
+
+ app = Flask(__name__)
+ app.session_interface = MySessionInterface()
+
+ .. versionadded:: 0.8
+ """
+
+ #: :meth:`make_null_session` will look here for the class that should
+ #: be created when a null session is requested. Likewise the
+ #: :meth:`is_null_session` method will perform a typecheck against
+ #: this type.
+ null_session_class = NullSession
+
+ def make_null_session(self, app):
+ """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.
+
+ This creates an instance of :attr:`null_session_class` by default.
+ """
+ return self.null_session_class()
+
+ def is_null_session(self, obj):
+ """Checks if a given object is a null session. Null sessions are
+ not asked to be saved.
+
+ This checks if the object is an instance of :attr:`null_session_class`
+ by default.
+ """
+ return isinstance(obj, self.null_session_class)
+
+ def get_cookie_domain(self, app):
+ """Helpful helper method that returns the cookie domain that should
+ be used for the session cookie if session cookies are used.
+ """
+ if app.config['SESSION_COOKIE_DOMAIN'] is not None:
+ return app.config['SESSION_COOKIE_DOMAIN']
+ if app.config['SERVER_NAME'] is not None:
+ # chop of the port which is usually not supported by browsers
+ return '.' + app.config['SERVER_NAME'].rsplit(':', 1)[0]
+
+ def get_cookie_path(self, app):
+ """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's set, and falls back to ``APPLICATION_ROOT`` or
+ uses ``/`` if it's `None`.
+ """
+ return app.config['SESSION_COOKIE_PATH'] or \
+ app.config['APPLICATION_ROOT'] or '/'
+
+ def get_cookie_httponly(self, app):
+ """Returns True if the session cookie should be httponly. This
+ currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
+ config var.
+ """
+ return app.config['SESSION_COOKIE_HTTPONLY']
+
+ def get_cookie_secure(self, app):
+ """Returns True if the cookie should be secure. This currently
+ just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
+ """
+ return app.config['SESSION_COOKIE_SECURE']
+
+ def get_expiration_time(self, app, session):
+ """A helper method that returns an expiration date for the session
+ or `None` if the session is linked to the browser session. The
+ default implementation returns now + the permanent session
+ lifetime configured on the application.
+ """
+ if session.permanent:
+ return datetime.utcnow() + app.permanent_session_lifetime
+
+ def open_session(self, app, request):
+ """This method has to be implemented and must either return `None`
+ 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 :class:`SessionMixin`.
+ """
+ raise NotImplementedError()
+
+ def save_session(self, app, session, response):
+ """This is called for actual sessions returned by :meth:`open_session`
+ 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.
+ """
+ raise NotImplementedError()
+
+
+class SecureCookieSessionInterface(SessionInterface):
+ """The cookie session interface that uses the Werkzeug securecookie
+ as client side session backend.
+ """
+ session_class = SecureCookieSession
+
+ def open_session(self, app, request):
+ key = app.secret_key
+ if key is not None:
+ return self.session_class.load_cookie(request,
+ app.session_cookie_name,
+ secret_key=key)
+
+ def save_session(self, app, session, response):
+ expires = self.get_expiration_time(app, session)
+ domain = self.get_cookie_domain(app)
+ path = self.get_cookie_path(app)
+ httponly = self.get_cookie_httponly(app)
+ secure = self.get_cookie_secure(app)
+ if session.modified and not session:
+ response.delete_cookie(app.session_cookie_name, path=path,
+ domain=domain)
+ else:
+ session.save_cookie(response, app.session_cookie_name, path=path,
+ expires=expires, httponly=httponly,
+ secure=secure, domain=domain)
diff --git a/websdk/flask/signals.py b/websdk/flask/signals.py
new file mode 100644
index 0000000..eeb763d
--- /dev/null
+++ b/websdk/flask/signals.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.signals
+ ~~~~~~~~~~~~~
+
+ Implements signals based on blinker if available, otherwise
+ falls silently back to a noop
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+signals_available = False
+try:
+ from blinker import Namespace
+ signals_available = True
+except ImportError:
+ class Namespace(object):
+ def signal(self, name, doc=None):
+ return _FakeSignal(name, doc)
+
+ class _FakeSignal(object):
+ """If blinker is unavailable, create a fake class with the same
+ interface that allows sending of signals but will fail with an
+ error on anything else. Instead of doing anything on send, it
+ will just ignore the arguments and do nothing instead.
+ """
+
+ def __init__(self, name, doc=None):
+ self.name = name
+ self.__doc__ = doc
+ def _fail(self, *args, **kwargs):
+ raise RuntimeError('signalling support is unavailable '
+ 'because the blinker library is '
+ 'not installed.')
+ send = lambda *a, **kw: None
+ connect = disconnect = has_receivers_for = receivers_for = \
+ temporarily_connected_to = connected_to = _fail
+ del _fail
+
+# the namespace for code signals. If you are not flask code, do
+# not put signals in here. Create your own namespace instead.
+_signals = Namespace()
+
+
+# core signals. For usage examples grep the sourcecode or consult
+# the API documentation in docs/api.rst as well as docs/signals.rst
+template_rendered = _signals.signal('template-rendered')
+request_started = _signals.signal('request-started')
+request_finished = _signals.signal('request-finished')
+request_tearing_down = _signals.signal('request-tearing-down')
+got_request_exception = _signals.signal('got-request-exception')
diff --git a/websdk/flask/templating.py b/websdk/flask/templating.py
new file mode 100644
index 0000000..90e8772
--- /dev/null
+++ b/websdk/flask/templating.py
@@ -0,0 +1,138 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.templating
+ ~~~~~~~~~~~~~~~~
+
+ Implements the bridge to Jinja2.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import posixpath
+from jinja2 import BaseLoader, Environment as BaseEnvironment, \
+ TemplateNotFound
+
+from .globals import _request_ctx_stack
+from .signals import template_rendered
+from .module import blueprint_is_module
+
+
+def _default_template_ctx_processor():
+ """Default template context processor. Injects `request`,
+ `session` and `g`.
+ """
+ reqctx = _request_ctx_stack.top
+ return dict(
+ config=reqctx.app.config,
+ request=reqctx.request,
+ session=reqctx.session,
+ g=reqctx.g
+ )
+
+
+class Environment(BaseEnvironment):
+ """Works like a regular Jinja2 environment but has some additional
+ knowledge of how Flask's blueprint works so that it can prepend the
+ name of the blueprint to referenced templates if necessary.
+ """
+
+ def __init__(self, app, **options):
+ if 'loader' not in options:
+ options['loader'] = app.create_global_jinja_loader()
+ BaseEnvironment.__init__(self, **options)
+ self.app = app
+
+
+class DispatchingJinjaLoader(BaseLoader):
+ """A loader that looks for templates in the application and all
+ the blueprint folders.
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def get_source(self, environment, template):
+ for loader, local_name in self._iter_loaders(template):
+ try:
+ return loader.get_source(environment, local_name)
+ except TemplateNotFound:
+ pass
+
+ raise TemplateNotFound(template)
+
+ def _iter_loaders(self, template):
+ loader = self.app.jinja_loader
+ if loader is not None:
+ yield loader, template
+
+ # old style module based loaders in case we are dealing with a
+ # blueprint that is an old style module
+ try:
+ module, local_name = posixpath.normpath(template).split('/', 1)
+ blueprint = self.app.blueprints[module]
+ if blueprint_is_module(blueprint):
+ loader = blueprint.jinja_loader
+ if loader is not None:
+ yield loader, local_name
+ except (ValueError, KeyError):
+ pass
+
+ for blueprint in self.app.blueprints.itervalues():
+ if blueprint_is_module(blueprint):
+ continue
+ loader = blueprint.jinja_loader
+ if loader is not None:
+ yield loader, template
+
+ def list_templates(self):
+ result = set()
+ loader = self.app.jinja_loader
+ if loader is not None:
+ result.update(loader.list_templates())
+
+ for name, blueprint in self.app.blueprints.iteritems():
+ loader = blueprint.jinja_loader
+ if loader is not None:
+ for template in loader.list_templates():
+ prefix = ''
+ if blueprint_is_module(blueprint):
+ prefix = name + '/'
+ result.add(prefix + template)
+
+ return list(result)
+
+
+def _render(template, context, app):
+ """Renders the template and fires the signal"""
+ rv = template.render(context)
+ template_rendered.send(app, template=template, context=context)
+ return rv
+
+
+def render_template(template_name, **context):
+ """Renders a template from the template folder with the given
+ context.
+
+ :param template_name: the name of the template to be rendered
+ :param context: the variables that should be available in the
+ context of the template.
+ """
+ ctx = _request_ctx_stack.top
+ ctx.app.update_template_context(context)
+ return _render(ctx.app.jinja_env.get_template(template_name),
+ context, ctx.app)
+
+
+def render_template_string(source, **context):
+ """Renders a template from the given template source string
+ with the given context.
+
+ :param template_name: the sourcecode of the template to be
+ rendered
+ :param context: the variables that should be available in the
+ context of the template.
+ """
+ ctx = _request_ctx_stack.top
+ ctx.app.update_template_context(context)
+ return _render(ctx.app.jinja_env.from_string(source),
+ context, ctx.app)
diff --git a/websdk/flask/testing.py b/websdk/flask/testing.py
new file mode 100644
index 0000000..782b40f
--- /dev/null
+++ b/websdk/flask/testing.py
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testing
+ ~~~~~~~~~~~~~
+
+ Implements test support helpers. This module is lazily imported
+ and usually not used in production environments.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+from contextlib import contextmanager
+from werkzeug.test import Client, EnvironBuilder
+from flask import _request_ctx_stack
+
+
+def make_test_environ_builder(app, path='/', base_url=None, *args, **kwargs):
+ """Creates a new test builder with some application defaults thrown in."""
+ http_host = app.config.get('SERVER_NAME')
+ app_root = app.config.get('APPLICATION_ROOT')
+ if base_url is None:
+ base_url = 'http://%s/' % (http_host or 'localhost')
+ if app_root:
+ base_url += app_root.lstrip('/')
+ return EnvironBuilder(path, base_url, *args, **kwargs)
+
+
+class FlaskClient(Client):
+ """Works like a regular Werkzeug test client but has some knowledge about
+ how Flask works to defer the cleanup of the request context stack to the
+ end of a with body when used in a with statement. For general information
+ about how to use this class refer to :class:`werkzeug.test.Client`.
+
+ Basic usage is outlined in the :ref:`testing` chapter.
+ """
+
+ preserve_context = False
+
+ @contextmanager
+ def session_transaction(self, *args, **kwargs):
+ """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.
+
+ with client.session_transaction() as session:
+ session['value'] = 42
+
+ 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
+ :meth:`~flask.Flask.test_request_context` which are directly
+ passed through.
+ """
+ if self.cookie_jar is None:
+ raise RuntimeError('Session transactions only make sense '
+ 'with cookies enabled.')
+ app = self.application
+ environ_overrides = kwargs.setdefault('environ_overrides', {})
+ self.cookie_jar.inject_wsgi(environ_overrides)
+ outer_reqctx = _request_ctx_stack.top
+ with app.test_request_context(*args, **kwargs) as c:
+ sess = app.open_session(c.request)
+ if sess is None:
+ raise RuntimeError('Session backend did not open a session. '
+ 'Check the configuration')
+
+ # Since we have to open a new request context for the session
+ # handling we want to make sure that we hide out own context
+ # from the caller. By pushing the original request context
+ # (or None) on top of this and popping it we get exactly that
+ # behavior. It's important to not use the push and pop
+ # methods of the actual request context object since that would
+ # mean that cleanup handlers are called
+ _request_ctx_stack.push(outer_reqctx)
+ try:
+ yield sess
+ finally:
+ _request_ctx_stack.pop()
+
+ resp = app.response_class()
+ if not app.session_interface.is_null_session(sess):
+ app.save_session(sess, resp)
+ headers = resp.get_wsgi_headers(c.request.environ)
+ self.cookie_jar.extract_wsgi(c.request.environ, headers)
+
+ def open(self, *args, **kwargs):
+ kwargs.setdefault('environ_overrides', {}) \
+ ['flask._preserve_context'] = self.preserve_context
+
+ as_tuple = kwargs.pop('as_tuple', False)
+ buffered = kwargs.pop('buffered', False)
+ follow_redirects = kwargs.pop('follow_redirects', False)
+ builder = make_test_environ_builder(self.application, *args, **kwargs)
+
+ return Client.open(self, builder,
+ as_tuple=as_tuple,
+ buffered=buffered,
+ follow_redirects=follow_redirects)
+
+ def __enter__(self):
+ if self.preserve_context:
+ raise RuntimeError('Cannot nest client invocations')
+ self.preserve_context = True
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ self.preserve_context = False
+
+ # on exit we want to clean up earlier. Normally the request context
+ # stays preserved until the next request in the same thread comes
+ # in. See RequestGlobals.push() for the general behavior.
+ top = _request_ctx_stack.top
+ if top is not None and top.preserved:
+ top.pop()
diff --git a/websdk/flask/testsuite/__init__.py b/websdk/flask/testsuite/__init__.py
new file mode 100644
index 0000000..76a4d72
--- /dev/null
+++ b/websdk/flask/testsuite/__init__.py
@@ -0,0 +1,221 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite
+ ~~~~~~~~~~~~~~~
+
+ Tests Flask itself. The majority of Flask is already tested
+ as part of Werkzeug.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import os
+import sys
+import flask
+import warnings
+import unittest
+from StringIO import StringIO
+from functools import update_wrapper
+from contextlib import contextmanager
+from werkzeug.utils import import_string, find_modules
+
+
+def add_to_path(path):
+ """Adds an entry to sys.path if it's not already there. This does
+ not append it but moves it to the front so that we can be sure it
+ is loaded.
+ """
+ if not os.path.isdir(path):
+ raise RuntimeError('Tried to add nonexisting path')
+
+ def _samefile(x, y):
+ try:
+ return os.path.samefile(x, y)
+ except (IOError, OSError):
+ return False
+ sys.path[:] = [x for x in sys.path if not _samefile(path, x)]
+ sys.path.insert(0, path)
+
+
+def iter_suites():
+ """Yields all testsuites."""
+ for module in find_modules(__name__):
+ mod = import_string(module)
+ if hasattr(mod, 'suite'):
+ yield mod.suite()
+
+
+def find_all_tests(suite):
+ """Yields all the tests and their names from a given suite."""
+ suites = [suite]
+ while suites:
+ s = suites.pop()
+ try:
+ suites.extend(s)
+ except TypeError:
+ yield s, '%s.%s.%s' % (
+ s.__class__.__module__,
+ s.__class__.__name__,
+ s._testMethodName
+ )
+
+
+@contextmanager
+def catch_warnings():
+ """Catch warnings in a with block in a list"""
+ # make sure deprecation warnings are active in tests
+ warnings.simplefilter('default', category=DeprecationWarning)
+
+ filters = warnings.filters
+ warnings.filters = filters[:]
+ old_showwarning = warnings.showwarning
+ log = []
+ def showwarning(message, category, filename, lineno, file=None, line=None):
+ log.append(locals())
+ try:
+ warnings.showwarning = showwarning
+ yield log
+ finally:
+ warnings.filters = filters
+ warnings.showwarning = old_showwarning
+
+
+@contextmanager
+def catch_stderr():
+ """Catch stderr in a StringIO"""
+ old_stderr = sys.stderr
+ sys.stderr = rv = StringIO()
+ try:
+ yield rv
+ finally:
+ sys.stderr = old_stderr
+
+
+def emits_module_deprecation_warning(f):
+ def new_f(self, *args, **kwargs):
+ with catch_warnings() as log:
+ f(self, *args, **kwargs)
+ self.assert_(log, 'expected deprecation warning')
+ for entry in log:
+ self.assert_('Modules are deprecated' in str(entry['message']))
+ return update_wrapper(new_f, f)
+
+
+class FlaskTestCase(unittest.TestCase):
+ """Baseclass for all the tests that Flask uses. Use these methods
+ for testing instead of the camelcased ones in the baseclass for
+ consistency.
+ """
+
+ def ensure_clean_request_context(self):
+ # make sure we're not leaking a request context since we are
+ # testing flask internally in debug mode in a few cases
+ self.assert_equal(flask._request_ctx_stack.top, None)
+
+ def setup(self):
+ pass
+
+ def teardown(self):
+ pass
+
+ def setUp(self):
+ self.setup()
+
+ def tearDown(self):
+ unittest.TestCase.tearDown(self)
+ self.ensure_clean_request_context()
+ self.teardown()
+
+ def assert_equal(self, x, y):
+ return self.assertEqual(x, y)
+
+ def assert_raises(self, exc_type, callable=None, *args, **kwargs):
+ catcher = _ExceptionCatcher(self, exc_type)
+ if callable is None:
+ return catcher
+ with catcher:
+ callable(*args, **kwargs)
+
+
+class _ExceptionCatcher(object):
+
+ def __init__(self, test_case, exc_type):
+ self.test_case = test_case
+ self.exc_type = exc_type
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ exception_name = self.exc_type.__name__
+ if exc_type is None:
+ self.test_case.fail('Expected exception of type %r' %
+ exception_name)
+ elif not issubclass(exc_type, self.exc_type):
+ raise exc_type, exc_value, tb
+ return True
+
+
+class BetterLoader(unittest.TestLoader):
+ """A nicer loader that solves two problems. First of all we are setting
+ up tests from different sources and we're doing this programmatically
+ which breaks the default loading logic so this is required anyways.
+ Secondly this loader has a nicer interpolation for test names than the
+ default one so you can just do ``run-tests.py ViewTestCase`` and it
+ will work.
+ """
+
+ def getRootSuite(self):
+ return suite()
+
+ def loadTestsFromName(self, name, module=None):
+ root = self.getRootSuite()
+ if name == 'suite':
+ return root
+
+ all_tests = []
+ for testcase, testname in find_all_tests(root):
+ if testname == name or \
+ testname.endswith('.' + name) or \
+ ('.' + name + '.') in testname or \
+ testname.startswith(name + '.'):
+ all_tests.append(testcase)
+
+ if not all_tests:
+ raise LookupError('could not find test case for "%s"' % name)
+
+ if len(all_tests) == 1:
+ return all_tests[0]
+ rv = unittest.TestSuite()
+ for test in all_tests:
+ rv.addTest(test)
+ return rv
+
+
+def setup_path():
+ add_to_path(os.path.abspath(os.path.join(
+ os.path.dirname(__file__), 'test_apps')))
+
+
+def suite():
+ """A testsuite that has all the Flask tests. You can use this
+ function to integrate the Flask tests into your own testsuite
+ in case you want to test that monkeypatches to Flask do not
+ break it.
+ """
+ setup_path()
+ suite = unittest.TestSuite()
+ for other_suite in iter_suites():
+ suite.addTest(other_suite)
+ return suite
+
+
+def main():
+ """Runs the testsuite as command line application."""
+ try:
+ unittest.main(testLoader=BetterLoader(), defaultTest='suite')
+ except Exception, e:
+ print 'Error: %s' % e
diff --git a/websdk/flask/testsuite/basic.py b/websdk/flask/testsuite/basic.py
new file mode 100644
index 0000000..1733f0a
--- /dev/null
+++ b/websdk/flask/testsuite/basic.py
@@ -0,0 +1,1051 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.basic
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ The basic functionality.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import re
+import flask
+import unittest
+from datetime import datetime
+from threading import Thread
+from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning
+from werkzeug.exceptions import BadRequest, NotFound
+from werkzeug.http import parse_date
+
+
+class BasicFunctionalityTestCase(FlaskTestCase):
+
+ def test_options_work(self):
+ app = flask.Flask(__name__)
+ @app.route('/', methods=['GET', 'POST'])
+ def index():
+ return 'Hello World'
+ rv = app.test_client().open('/', method='OPTIONS')
+ self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS', 'POST'])
+ self.assert_equal(rv.data, '')
+
+ def test_options_on_multiple_rules(self):
+ app = flask.Flask(__name__)
+ @app.route('/', methods=['GET', 'POST'])
+ def index():
+ return 'Hello World'
+ @app.route('/', methods=['PUT'])
+ def index_put():
+ return 'Aha!'
+ rv = app.test_client().open('/', method='OPTIONS')
+ self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'])
+
+ def test_options_handling_disabled(self):
+ app = flask.Flask(__name__)
+ def index():
+ return 'Hello World!'
+ index.provide_automatic_options = False
+ app.route('/')(index)
+ rv = app.test_client().open('/', method='OPTIONS')
+ self.assert_equal(rv.status_code, 405)
+
+ app = flask.Flask(__name__)
+ def index2():
+ return 'Hello World!'
+ index2.provide_automatic_options = True
+ app.route('/', methods=['OPTIONS'])(index2)
+ rv = app.test_client().open('/', method='OPTIONS')
+ self.assert_equal(sorted(rv.allow), ['OPTIONS'])
+
+ def test_request_dispatching(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return flask.request.method
+ @app.route('/more', methods=['GET', 'POST'])
+ def more():
+ return flask.request.method
+
+ c = app.test_client()
+ self.assert_equal(c.get('/').data, 'GET')
+ rv = c.post('/')
+ self.assert_equal(rv.status_code, 405)
+ self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS'])
+ rv = c.head('/')
+ self.assert_equal(rv.status_code, 200)
+ self.assert_(not rv.data) # head truncates
+ self.assert_equal(c.post('/more').data, 'POST')
+ self.assert_equal(c.get('/more').data, 'GET')
+ rv = c.delete('/more')
+ self.assert_equal(rv.status_code, 405)
+ self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS', 'POST'])
+
+ def test_url_mapping(self):
+ app = flask.Flask(__name__)
+ def index():
+ return flask.request.method
+ def more():
+ return flask.request.method
+
+ app.add_url_rule('/', 'index', index)
+ app.add_url_rule('/more', 'more', more, methods=['GET', 'POST'])
+
+ c = app.test_client()
+ self.assert_equal(c.get('/').data, 'GET')
+ rv = c.post('/')
+ self.assert_equal(rv.status_code, 405)
+ self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS'])
+ rv = c.head('/')
+ self.assert_equal(rv.status_code, 200)
+ self.assert_(not rv.data) # head truncates
+ self.assert_equal(c.post('/more').data, 'POST')
+ self.assert_equal(c.get('/more').data, 'GET')
+ rv = c.delete('/more')
+ self.assert_equal(rv.status_code, 405)
+ self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS', 'POST'])
+
+ def test_werkzeug_routing(self):
+ from werkzeug.routing import Submount, Rule
+ app = flask.Flask(__name__)
+ app.url_map.add(Submount('/foo', [
+ Rule('/bar', endpoint='bar'),
+ Rule('/', endpoint='index')
+ ]))
+ def bar():
+ return 'bar'
+ def index():
+ return 'index'
+ app.view_functions['bar'] = bar
+ app.view_functions['index'] = index
+
+ c = app.test_client()
+ self.assert_equal(c.get('/foo/').data, 'index')
+ self.assert_equal(c.get('/foo/bar').data, 'bar')
+
+ def test_endpoint_decorator(self):
+ from werkzeug.routing import Submount, Rule
+ app = flask.Flask(__name__)
+ app.url_map.add(Submount('/foo', [
+ Rule('/bar', endpoint='bar'),
+ Rule('/', endpoint='index')
+ ]))
+
+ @app.endpoint('bar')
+ def bar():
+ return 'bar'
+
+ @app.endpoint('index')
+ def index():
+ return 'index'
+
+ c = app.test_client()
+ self.assert_equal(c.get('/foo/').data, 'index')
+ self.assert_equal(c.get('/foo/bar').data, 'bar')
+
+ def test_session(self):
+ app = flask.Flask(__name__)
+ app.secret_key = 'testkey'
+ @app.route('/set', methods=['POST'])
+ def set():
+ flask.session['value'] = flask.request.form['value']
+ return 'value set'
+ @app.route('/get')
+ def get():
+ return flask.session['value']
+
+ c = app.test_client()
+ self.assert_equal(c.post('/set', data={'value': '42'}).data, 'value set')
+ self.assert_equal(c.get('/get').data, '42')
+
+ def test_session_using_server_name(self):
+ app = flask.Flask(__name__)
+ app.config.update(
+ SECRET_KEY='foo',
+ SERVER_NAME='example.com'
+ )
+ @app.route('/')
+ def index():
+ flask.session['testing'] = 42
+ return 'Hello World'
+ rv = app.test_client().get('/', 'http://example.com/')
+ self.assert_('domain=.example.com' in rv.headers['set-cookie'].lower())
+ self.assert_('httponly' in rv.headers['set-cookie'].lower())
+
+ def test_session_using_server_name_and_port(self):
+ app = flask.Flask(__name__)
+ app.config.update(
+ SECRET_KEY='foo',
+ SERVER_NAME='example.com:8080'
+ )
+ @app.route('/')
+ def index():
+ flask.session['testing'] = 42
+ return 'Hello World'
+ rv = app.test_client().get('/', 'http://example.com:8080/')
+ self.assert_('domain=.example.com' in rv.headers['set-cookie'].lower())
+ self.assert_('httponly' in rv.headers['set-cookie'].lower())
+
+ def test_session_using_application_root(self):
+ class PrefixPathMiddleware(object):
+ def __init__(self, app, prefix):
+ self.app = app
+ self.prefix = prefix
+ def __call__(self, environ, start_response):
+ environ['SCRIPT_NAME'] = self.prefix
+ return self.app(environ, start_response)
+
+ app = flask.Flask(__name__)
+ app.wsgi_app = PrefixPathMiddleware(app.wsgi_app, '/bar')
+ app.config.update(
+ SECRET_KEY='foo',
+ APPLICATION_ROOT='/bar'
+ )
+ @app.route('/')
+ def index():
+ flask.session['testing'] = 42
+ return 'Hello World'
+ rv = app.test_client().get('/', 'http://example.com:8080/')
+ self.assert_('path=/bar' in rv.headers['set-cookie'].lower())
+
+ def test_session_using_session_settings(self):
+ app = flask.Flask(__name__)
+ app.config.update(
+ SECRET_KEY='foo',
+ SERVER_NAME='www.example.com:8080',
+ APPLICATION_ROOT='/test',
+ SESSION_COOKIE_DOMAIN='.example.com',
+ SESSION_COOKIE_HTTPONLY=False,
+ SESSION_COOKIE_SECURE=True,
+ SESSION_COOKIE_PATH='/'
+ )
+ @app.route('/')
+ def index():
+ flask.session['testing'] = 42
+ return 'Hello World'
+ rv = app.test_client().get('/', 'http://www.example.com:8080/test/')
+ cookie = rv.headers['set-cookie'].lower()
+ self.assert_('domain=.example.com' in cookie)
+ self.assert_('path=/;' in cookie)
+ self.assert_('secure' in cookie)
+ self.assert_('httponly' not in cookie)
+
+ def test_missing_session(self):
+ app = flask.Flask(__name__)
+ def expect_exception(f, *args, **kwargs):
+ try:
+ f(*args, **kwargs)
+ except RuntimeError, e:
+ self.assert_(e.args and 'session is unavailable' in e.args[0])
+ else:
+ self.assert_(False, 'expected exception')
+ with app.test_request_context():
+ self.assert_(flask.session.get('missing_key') is None)
+ expect_exception(flask.session.__setitem__, 'foo', 42)
+ expect_exception(flask.session.pop, 'foo')
+
+ def test_session_expiration(self):
+ permanent = True
+ app = flask.Flask(__name__)
+ app.secret_key = 'testkey'
+ @app.route('/')
+ def index():
+ flask.session['test'] = 42
+ flask.session.permanent = permanent
+ return ''
+
+ @app.route('/test')
+ def test():
+ return unicode(flask.session.permanent)
+
+ client = app.test_client()
+ rv = client.get('/')
+ self.assert_('set-cookie' in rv.headers)
+ match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
+ expires = parse_date(match.group())
+ expected = datetime.utcnow() + app.permanent_session_lifetime
+ self.assert_equal(expires.year, expected.year)
+ self.assert_equal(expires.month, expected.month)
+ self.assert_equal(expires.day, expected.day)
+
+ rv = client.get('/test')
+ self.assert_equal(rv.data, 'True')
+
+ permanent = False
+ rv = app.test_client().get('/')
+ self.assert_('set-cookie' in rv.headers)
+ match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie'])
+ self.assert_(match is None)
+
+ def test_flashes(self):
+ app = flask.Flask(__name__)
+ app.secret_key = 'testkey'
+
+ with app.test_request_context():
+ self.assert_(not flask.session.modified)
+ flask.flash('Zap')
+ flask.session.modified = False
+ flask.flash('Zip')
+ self.assert_(flask.session.modified)
+ self.assert_equal(list(flask.get_flashed_messages()), ['Zap', 'Zip'])
+
+ def test_extended_flashing(self):
+ app = flask.Flask(__name__)
+ app.secret_key = 'testkey'
+
+ @app.route('/')
+ def index():
+ flask.flash(u'Hello World')
+ flask.flash(u'Hello World', 'error')
+ flask.flash(flask.Markup(u'<em>Testing</em>'), 'warning')
+ return ''
+
+ @app.route('/test')
+ def test():
+ messages = flask.get_flashed_messages(with_categories=True)
+ self.assert_equal(len(messages), 3)
+ self.assert_equal(messages[0], ('message', u'Hello World'))
+ self.assert_equal(messages[1], ('error', u'Hello World'))
+ self.assert_equal(messages[2], ('warning', flask.Markup(u'<em>Testing</em>')))
+ return ''
+ messages = flask.get_flashed_messages()
+ self.assert_equal(len(messages), 3)
+ self.assert_equal(messages[0], u'Hello World')
+ self.assert_equal(messages[1], u'Hello World')
+ self.assert_equal(messages[2], flask.Markup(u'<em>Testing</em>'))
+
+ c = app.test_client()
+ c.get('/')
+ c.get('/test')
+
+ def test_request_processing(self):
+ app = flask.Flask(__name__)
+ evts = []
+ @app.before_request
+ def before_request():
+ evts.append('before')
+ @app.after_request
+ def after_request(response):
+ response.data += '|after'
+ evts.append('after')
+ return response
+ @app.route('/')
+ def index():
+ self.assert_('before' in evts)
+ self.assert_('after' not in evts)
+ return 'request'
+ self.assert_('after' not in evts)
+ rv = app.test_client().get('/').data
+ self.assert_('after' in evts)
+ self.assert_equal(rv, 'request|after')
+
+ def test_teardown_request_handler(self):
+ called = []
+ app = flask.Flask(__name__)
+ @app.teardown_request
+ def teardown_request(exc):
+ called.append(True)
+ return "Ignored"
+ @app.route('/')
+ def root():
+ return "Response"
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.status_code, 200)
+ self.assert_('Response' in rv.data)
+ self.assert_equal(len(called), 1)
+
+ def test_teardown_request_handler_debug_mode(self):
+ called = []
+ app = flask.Flask(__name__)
+ app.testing = True
+ @app.teardown_request
+ def teardown_request(exc):
+ called.append(True)
+ return "Ignored"
+ @app.route('/')
+ def root():
+ return "Response"
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.status_code, 200)
+ self.assert_('Response' in rv.data)
+ self.assert_equal(len(called), 1)
+
+ def test_teardown_request_handler_error(self):
+ called = []
+ app = flask.Flask(__name__)
+ @app.teardown_request
+ def teardown_request1(exc):
+ self.assert_equal(type(exc), ZeroDivisionError)
+ called.append(True)
+ # This raises a new error and blows away sys.exc_info(), so we can
+ # test that all teardown_requests get passed the same original
+ # exception.
+ try:
+ raise TypeError
+ except:
+ pass
+ @app.teardown_request
+ def teardown_request2(exc):
+ self.assert_equal(type(exc), ZeroDivisionError)
+ called.append(True)
+ # This raises a new error and blows away sys.exc_info(), so we can
+ # test that all teardown_requests get passed the same original
+ # exception.
+ try:
+ raise TypeError
+ except:
+ pass
+ @app.route('/')
+ def fails():
+ 1/0
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.status_code, 500)
+ self.assert_('Internal Server Error' in rv.data)
+ self.assert_equal(len(called), 2)
+
+ def test_before_after_request_order(self):
+ called = []
+ app = flask.Flask(__name__)
+ @app.before_request
+ def before1():
+ called.append(1)
+ @app.before_request
+ def before2():
+ called.append(2)
+ @app.after_request
+ def after1(response):
+ called.append(4)
+ return response
+ @app.after_request
+ def after2(response):
+ called.append(3)
+ return response
+ @app.teardown_request
+ def finish1(exc):
+ called.append(6)
+ @app.teardown_request
+ def finish2(exc):
+ called.append(5)
+ @app.route('/')
+ def index():
+ return '42'
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, '42')
+ self.assert_equal(called, [1, 2, 3, 4, 5, 6])
+
+ def test_error_handling(self):
+ app = flask.Flask(__name__)
+ @app.errorhandler(404)
+ def not_found(e):
+ return 'not found', 404
+ @app.errorhandler(500)
+ def internal_server_error(e):
+ return 'internal server error', 500
+ @app.route('/')
+ def index():
+ flask.abort(404)
+ @app.route('/error')
+ def error():
+ 1 // 0
+ c = app.test_client()
+ rv = c.get('/')
+ self.assert_equal(rv.status_code, 404)
+ self.assert_equal(rv.data, 'not found')
+ rv = c.get('/error')
+ self.assert_equal(rv.status_code, 500)
+ self.assert_equal('internal server error', rv.data)
+
+ def test_before_request_and_routing_errors(self):
+ app = flask.Flask(__name__)
+ @app.before_request
+ def attach_something():
+ flask.g.something = 'value'
+ @app.errorhandler(404)
+ def return_something(error):
+ return flask.g.something, 404
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.status_code, 404)
+ self.assert_equal(rv.data, 'value')
+
+ def test_user_error_handling(self):
+ class MyException(Exception):
+ pass
+
+ app = flask.Flask(__name__)
+ @app.errorhandler(MyException)
+ def handle_my_exception(e):
+ self.assert_(isinstance(e, MyException))
+ return '42'
+ @app.route('/')
+ def index():
+ raise MyException()
+
+ c = app.test_client()
+ self.assert_equal(c.get('/').data, '42')
+
+ def test_trapping_of_bad_request_key_errors(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ @app.route('/fail')
+ def fail():
+ flask.request.form['missing_key']
+ c = app.test_client()
+ self.assert_equal(c.get('/fail').status_code, 400)
+
+ app.config['TRAP_BAD_REQUEST_ERRORS'] = True
+ c = app.test_client()
+ try:
+ c.get('/fail')
+ except KeyError, e:
+ self.assert_(isinstance(e, BadRequest))
+ else:
+ self.fail('Expected exception')
+
+ def test_trapping_of_all_http_exceptions(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ app.config['TRAP_HTTP_EXCEPTIONS'] = True
+ @app.route('/fail')
+ def fail():
+ flask.abort(404)
+
+ c = app.test_client()
+ try:
+ c.get('/fail')
+ except NotFound, e:
+ pass
+ else:
+ self.fail('Expected exception')
+
+ def test_enctype_debug_helper(self):
+ from flask.debughelpers import DebugFilesKeyError
+ app = flask.Flask(__name__)
+ app.debug = True
+ @app.route('/fail', methods=['POST'])
+ def index():
+ return flask.request.files['foo'].filename
+
+ # with statement is important because we leave an exception on the
+ # stack otherwise and we want to ensure that this is not the case
+ # to not negatively affect other tests.
+ with app.test_client() as c:
+ try:
+ c.post('/fail', data={'foo': 'index.txt'})
+ except DebugFilesKeyError, e:
+ self.assert_('no file contents were transmitted' in str(e))
+ self.assert_('This was submitted: "index.txt"' in str(e))
+ else:
+ self.fail('Expected exception')
+
+ def test_teardown_on_pop(self):
+ buffer = []
+ app = flask.Flask(__name__)
+ @app.teardown_request
+ def end_of_request(exception):
+ buffer.append(exception)
+
+ ctx = app.test_request_context()
+ ctx.push()
+ self.assert_equal(buffer, [])
+ ctx.pop()
+ self.assert_equal(buffer, [None])
+
+ def test_response_creation(self):
+ app = flask.Flask(__name__)
+ @app.route('/unicode')
+ def from_unicode():
+ return u'Hällo Wörld'
+ @app.route('/string')
+ def from_string():
+ return u'Hällo Wörld'.encode('utf-8')
+ @app.route('/args')
+ def from_tuple():
+ return 'Meh', 400, {'X-Foo': 'Testing'}, 'text/plain'
+ c = app.test_client()
+ self.assert_equal(c.get('/unicode').data, u'Hällo Wörld'.encode('utf-8'))
+ self.assert_equal(c.get('/string').data, u'Hällo Wörld'.encode('utf-8'))
+ rv = c.get('/args')
+ self.assert_equal(rv.data, 'Meh')
+ self.assert_equal(rv.headers['X-Foo'], 'Testing')
+ self.assert_equal(rv.status_code, 400)
+ self.assert_equal(rv.mimetype, 'text/plain')
+
+ def test_make_response(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ rv = flask.make_response()
+ self.assert_equal(rv.status_code, 200)
+ self.assert_equal(rv.data, '')
+ self.assert_equal(rv.mimetype, 'text/html')
+
+ rv = flask.make_response('Awesome')
+ self.assert_equal(rv.status_code, 200)
+ self.assert_equal(rv.data, 'Awesome')
+ self.assert_equal(rv.mimetype, 'text/html')
+
+ rv = flask.make_response('W00t', 404)
+ self.assert_equal(rv.status_code, 404)
+ self.assert_equal(rv.data, 'W00t')
+ self.assert_equal(rv.mimetype, 'text/html')
+
+ def test_url_generation(self):
+ app = flask.Flask(__name__)
+ @app.route('/hello/<name>', methods=['POST'])
+ def hello():
+ pass
+ with app.test_request_context():
+ self.assert_equal(flask.url_for('hello', name='test x'), '/hello/test%20x')
+ self.assert_equal(flask.url_for('hello', name='test x', _external=True),
+ 'http://localhost/hello/test%20x')
+
+ def test_custom_converters(self):
+ from werkzeug.routing import BaseConverter
+ class ListConverter(BaseConverter):
+ def to_python(self, value):
+ return value.split(',')
+ def to_url(self, value):
+ base_to_url = super(ListConverter, self).to_url
+ return ','.join(base_to_url(x) for x in value)
+ app = flask.Flask(__name__)
+ app.url_map.converters['list'] = ListConverter
+ @app.route('/<list:args>')
+ def index(args):
+ return '|'.join(args)
+ c = app.test_client()
+ self.assert_equal(c.get('/1,2,3').data, '1|2|3')
+
+ def test_static_files(self):
+ app = flask.Flask(__name__)
+ rv = app.test_client().get('/static/index.html')
+ self.assert_equal(rv.status_code, 200)
+ self.assert_equal(rv.data.strip(), '<h1>Hello World!</h1>')
+ with app.test_request_context():
+ self.assert_equal(flask.url_for('static', filename='index.html'),
+ '/static/index.html')
+
+ def test_none_response(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def test():
+ return None
+ try:
+ app.test_client().get('/')
+ except ValueError, e:
+ self.assert_equal(str(e), 'View function did not return a response')
+ pass
+ else:
+ self.assert_("Expected ValueError")
+
+ def test_request_locals(self):
+ self.assert_equal(repr(flask.g), '<LocalProxy unbound>')
+ self.assertFalse(flask.g)
+
+ def test_proper_test_request_context(self):
+ app = flask.Flask(__name__)
+ app.config.update(
+ SERVER_NAME='localhost.localdomain:5000'
+ )
+
+ @app.route('/')
+ def index():
+ return None
+
+ @app.route('/', subdomain='foo')
+ def sub():
+ return None
+
+ with app.test_request_context('/'):
+ self.assert_equal(flask.url_for('index', _external=True), 'http://localhost.localdomain:5000/')
+
+ with app.test_request_context('/'):
+ self.assert_equal(flask.url_for('sub', _external=True), 'http://foo.localhost.localdomain:5000/')
+
+ try:
+ with app.test_request_context('/', environ_overrides={'HTTP_HOST': 'localhost'}):
+ pass
+ except Exception, e:
+ self.assert_(isinstance(e, ValueError))
+ self.assert_equal(str(e), "the server name provided " +
+ "('localhost.localdomain:5000') does not match the " + \
+ "server name from the WSGI environment ('localhost')")
+
+ try:
+ app.config.update(SERVER_NAME='localhost')
+ with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost'}):
+ pass
+ except ValueError, e:
+ raise ValueError(
+ "No ValueError exception should have been raised \"%s\"" % e
+ )
+
+ try:
+ app.config.update(SERVER_NAME='localhost:80')
+ with app.test_request_context('/', environ_overrides={'SERVER_NAME': 'localhost:80'}):
+ pass
+ except ValueError, e:
+ raise ValueError(
+ "No ValueError exception should have been raised \"%s\"" % e
+ )
+
+ def test_test_app_proper_environ(self):
+ app = flask.Flask(__name__)
+ app.config.update(
+ SERVER_NAME='localhost.localdomain:5000'
+ )
+ @app.route('/')
+ def index():
+ return 'Foo'
+
+ @app.route('/', subdomain='foo')
+ def subdomain():
+ return 'Foo SubDomain'
+
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, 'Foo')
+
+ rv = app.test_client().get('/', 'http://localhost.localdomain:5000')
+ self.assert_equal(rv.data, 'Foo')
+
+ rv = app.test_client().get('/', 'https://localhost.localdomain:5000')
+ self.assert_equal(rv.data, 'Foo')
+
+ app.config.update(SERVER_NAME='localhost.localdomain')
+ rv = app.test_client().get('/', 'https://localhost.localdomain')
+ self.assert_equal(rv.data, 'Foo')
+
+ try:
+ app.config.update(SERVER_NAME='localhost.localdomain:443')
+ rv = app.test_client().get('/', 'https://localhost.localdomain')
+ # Werkzeug 0.8
+ self.assert_equal(rv.status_code, 404)
+ except ValueError, e:
+ # Werkzeug 0.7
+ self.assert_equal(str(e), "the server name provided " +
+ "('localhost.localdomain:443') does not match the " + \
+ "server name from the WSGI environment ('localhost.localdomain')")
+
+ try:
+ app.config.update(SERVER_NAME='localhost.localdomain')
+ rv = app.test_client().get('/', 'http://foo.localhost')
+ # Werkzeug 0.8
+ self.assert_equal(rv.status_code, 404)
+ except ValueError, e:
+ # Werkzeug 0.7
+ self.assert_equal(str(e), "the server name provided " + \
+ "('localhost.localdomain') does not match the " + \
+ "server name from the WSGI environment ('foo.localhost')")
+
+ rv = app.test_client().get('/', 'http://foo.localhost.localdomain')
+ self.assert_equal(rv.data, 'Foo SubDomain')
+
+ def test_exception_propagation(self):
+ def apprunner(configkey):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ 1/0
+ c = app.test_client()
+ if config_key is not None:
+ app.config[config_key] = True
+ try:
+ resp = c.get('/')
+ except Exception:
+ pass
+ else:
+ self.fail('expected exception')
+ else:
+ self.assert_equal(c.get('/').status_code, 500)
+
+ # we have to run this test in an isolated thread because if the
+ # debug flag is set to true and an exception happens the context is
+ # not torn down. This causes other tests that run after this fail
+ # when they expect no exception on the stack.
+ for config_key in 'TESTING', 'PROPAGATE_EXCEPTIONS', 'DEBUG', None:
+ t = Thread(target=apprunner, args=(config_key,))
+ t.start()
+ t.join()
+
+ def test_max_content_length(self):
+ app = flask.Flask(__name__)
+ app.config['MAX_CONTENT_LENGTH'] = 64
+ @app.before_request
+ def always_first():
+ flask.request.form['myfile']
+ self.assert_(False)
+ @app.route('/accept', methods=['POST'])
+ def accept_file():
+ flask.request.form['myfile']
+ self.assert_(False)
+ @app.errorhandler(413)
+ def catcher(error):
+ return '42'
+
+ c = app.test_client()
+ rv = c.post('/accept', data={'myfile': 'foo' * 100})
+ self.assert_equal(rv.data, '42')
+
+ def test_url_processors(self):
+ app = flask.Flask(__name__)
+
+ @app.url_defaults
+ def add_language_code(endpoint, values):
+ if flask.g.lang_code is not None and \
+ app.url_map.is_endpoint_expecting(endpoint, 'lang_code'):
+ values.setdefault('lang_code', flask.g.lang_code)
+
+ @app.url_value_preprocessor
+ def pull_lang_code(endpoint, values):
+ flask.g.lang_code = values.pop('lang_code', None)
+
+ @app.route('/<lang_code>/')
+ def index():
+ return flask.url_for('about')
+
+ @app.route('/<lang_code>/about')
+ def about():
+ return flask.url_for('something_else')
+
+ @app.route('/foo')
+ def something_else():
+ return flask.url_for('about', lang_code='en')
+
+ c = app.test_client()
+
+ self.assert_equal(c.get('/de/').data, '/de/about')
+ self.assert_equal(c.get('/de/about').data, '/foo')
+ self.assert_equal(c.get('/foo').data, '/en/about')
+
+ def test_debug_mode_complains_after_first_request(self):
+ app = flask.Flask(__name__)
+ app.debug = True
+ @app.route('/')
+ def index():
+ return 'Awesome'
+ self.assert_(not app.got_first_request)
+ self.assert_equal(app.test_client().get('/').data, 'Awesome')
+ try:
+ @app.route('/foo')
+ def broken():
+ return 'Meh'
+ except AssertionError, e:
+ self.assert_('A setup function was called' in str(e))
+ else:
+ self.fail('Expected exception')
+
+ app.debug = False
+ @app.route('/foo')
+ def working():
+ return 'Meh'
+ self.assert_equal(app.test_client().get('/foo').data, 'Meh')
+ self.assert_(app.got_first_request)
+
+ def test_before_first_request_functions(self):
+ got = []
+ app = flask.Flask(__name__)
+ @app.before_first_request
+ def foo():
+ got.append(42)
+ c = app.test_client()
+ c.get('/')
+ self.assert_equal(got, [42])
+ c.get('/')
+ self.assert_equal(got, [42])
+ self.assert_(app.got_first_request)
+
+ def test_routing_redirect_debugging(self):
+ app = flask.Flask(__name__)
+ app.debug = True
+ @app.route('/foo/', methods=['GET', 'POST'])
+ def foo():
+ return 'success'
+ with app.test_client() as c:
+ try:
+ c.post('/foo', data={})
+ except AssertionError, e:
+ self.assert_('http://localhost/foo/' in str(e))
+ self.assert_('Make sure to directly send your POST-request '
+ 'to this URL' in str(e))
+ else:
+ self.fail('Expected exception')
+
+ rv = c.get('/foo', data={}, follow_redirects=True)
+ self.assert_equal(rv.data, 'success')
+
+ app.debug = False
+ with app.test_client() as c:
+ rv = c.post('/foo', data={}, follow_redirects=True)
+ self.assert_equal(rv.data, 'success')
+
+ def test_route_decorator_custom_endpoint(self):
+ app = flask.Flask(__name__)
+ app.debug = True
+
+ @app.route('/foo/')
+ def foo():
+ return flask.request.endpoint
+
+ @app.route('/bar/', endpoint='bar')
+ def for_bar():
+ return flask.request.endpoint
+
+ @app.route('/bar/123', endpoint='123')
+ def for_bar_foo():
+ return flask.request.endpoint
+
+ with app.test_request_context():
+ assert flask.url_for('foo') == '/foo/'
+ assert flask.url_for('bar') == '/bar/'
+ assert flask.url_for('123') == '/bar/123'
+
+ c = app.test_client()
+ self.assertEqual(c.get('/foo/').data, 'foo')
+ self.assertEqual(c.get('/bar/').data, 'bar')
+ self.assertEqual(c.get('/bar/123').data, '123')
+
+ def test_preserve_only_once(self):
+ app = flask.Flask(__name__)
+ app.debug = True
+
+ @app.route('/fail')
+ def fail_func():
+ 1/0
+
+ c = app.test_client()
+ for x in xrange(3):
+ with self.assert_raises(ZeroDivisionError):
+ c.get('/fail')
+
+ self.assert_(flask._request_ctx_stack.top is not None)
+ flask._request_ctx_stack.pop()
+ self.assert_(flask._request_ctx_stack.top is None)
+
+
+class ContextTestCase(FlaskTestCase):
+
+ def test_context_binding(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return 'Hello %s!' % flask.request.args['name']
+ @app.route('/meh')
+ def meh():
+ return flask.request.url
+
+ with app.test_request_context('/?name=World'):
+ self.assert_equal(index(), 'Hello World!')
+ with app.test_request_context('/meh'):
+ self.assert_equal(meh(), 'http://localhost/meh')
+ self.assert_(flask._request_ctx_stack.top is None)
+
+ def test_context_test(self):
+ app = flask.Flask(__name__)
+ self.assert_(not flask.request)
+ self.assert_(not flask.has_request_context())
+ ctx = app.test_request_context()
+ ctx.push()
+ try:
+ self.assert_(flask.request)
+ self.assert_(flask.has_request_context())
+ finally:
+ ctx.pop()
+
+ def test_manual_context_binding(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return 'Hello %s!' % flask.request.args['name']
+
+ ctx = app.test_request_context('/?name=World')
+ ctx.push()
+ self.assert_equal(index(), 'Hello World!')
+ ctx.pop()
+ try:
+ index()
+ except RuntimeError:
+ pass
+ else:
+ self.assert_(0, 'expected runtime error')
+
+
+class SubdomainTestCase(FlaskTestCase):
+
+ def test_basic_support(self):
+ app = flask.Flask(__name__)
+ app.config['SERVER_NAME'] = 'localhost'
+ @app.route('/')
+ def normal_index():
+ return 'normal index'
+ @app.route('/', subdomain='test')
+ def test_index():
+ return 'test index'
+
+ c = app.test_client()
+ rv = c.get('/', 'http://localhost/')
+ self.assert_equal(rv.data, 'normal index')
+
+ rv = c.get('/', 'http://test.localhost/')
+ self.assert_equal(rv.data, 'test index')
+
+ @emits_module_deprecation_warning
+ def test_module_static_path_subdomain(self):
+ app = flask.Flask(__name__)
+ app.config['SERVER_NAME'] = 'example.com'
+ from subdomaintestmodule import mod
+ app.register_module(mod)
+ c = app.test_client()
+ rv = c.get('/static/hello.txt', 'http://foo.example.com/')
+ self.assert_equal(rv.data.strip(), 'Hello Subdomain')
+
+ def test_subdomain_matching(self):
+ app = flask.Flask(__name__)
+ app.config['SERVER_NAME'] = 'localhost'
+ @app.route('/', subdomain='<user>')
+ def index(user):
+ return 'index for %s' % user
+
+ c = app.test_client()
+ rv = c.get('/', 'http://mitsuhiko.localhost/')
+ self.assert_equal(rv.data, 'index for mitsuhiko')
+
+ def test_subdomain_matching_with_ports(self):
+ app = flask.Flask(__name__)
+ app.config['SERVER_NAME'] = 'localhost:3000'
+ @app.route('/', subdomain='<user>')
+ def index(user):
+ return 'index for %s' % user
+
+ c = app.test_client()
+ rv = c.get('/', 'http://mitsuhiko.localhost:3000/')
+ self.assert_equal(rv.data, 'index for mitsuhiko')
+
+ @emits_module_deprecation_warning
+ def test_module_subdomain_support(self):
+ app = flask.Flask(__name__)
+ mod = flask.Module(__name__, 'test', subdomain='testing')
+ app.config['SERVER_NAME'] = 'localhost'
+
+ @mod.route('/test')
+ def test():
+ return 'Test'
+
+ @mod.route('/outside', subdomain='xtesting')
+ def bar():
+ return 'Outside'
+
+ app.register_module(mod)
+
+ c = app.test_client()
+ rv = c.get('/test', 'http://testing.localhost/')
+ self.assert_equal(rv.data, 'Test')
+ rv = c.get('/outside', 'http://xtesting.localhost/')
+ self.assert_equal(rv.data, 'Outside')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(BasicFunctionalityTestCase))
+ suite.addTest(unittest.makeSuite(ContextTestCase))
+ suite.addTest(unittest.makeSuite(SubdomainTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/blueprints.py b/websdk/flask/testsuite/blueprints.py
new file mode 100644
index 0000000..3f65dd4
--- /dev/null
+++ b/websdk/flask/testsuite/blueprints.py
@@ -0,0 +1,512 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.blueprints
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Blueprints (and currently modules)
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import flask
+import unittest
+import warnings
+from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning
+from werkzeug.exceptions import NotFound
+from jinja2 import TemplateNotFound
+
+
+# import moduleapp here because it uses deprecated features and we don't
+# want to see the warnings
+warnings.simplefilter('ignore', DeprecationWarning)
+from moduleapp import app as moduleapp
+warnings.simplefilter('default', DeprecationWarning)
+
+
+class ModuleTestCase(FlaskTestCase):
+
+ @emits_module_deprecation_warning
+ def test_basic_module(self):
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin', url_prefix='/admin')
+ @admin.route('/')
+ def admin_index():
+ return 'admin index'
+ @admin.route('/login')
+ def admin_login():
+ return 'admin login'
+ @admin.route('/logout')
+ def admin_logout():
+ return 'admin logout'
+ @app.route('/')
+ def index():
+ return 'the index'
+ app.register_module(admin)
+ c = app.test_client()
+ self.assert_equal(c.get('/').data, 'the index')
+ self.assert_equal(c.get('/admin/').data, 'admin index')
+ self.assert_equal(c.get('/admin/login').data, 'admin login')
+ self.assert_equal(c.get('/admin/logout').data, 'admin logout')
+
+ @emits_module_deprecation_warning
+ def test_default_endpoint_name(self):
+ app = flask.Flask(__name__)
+ mod = flask.Module(__name__, 'frontend')
+ def index():
+ return 'Awesome'
+ mod.add_url_rule('/', view_func=index)
+ app.register_module(mod)
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, 'Awesome')
+ with app.test_request_context():
+ self.assert_equal(flask.url_for('frontend.index'), '/')
+
+ @emits_module_deprecation_warning
+ def test_request_processing(self):
+ catched = []
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin', url_prefix='/admin')
+ @admin.before_request
+ def before_admin_request():
+ catched.append('before-admin')
+ @admin.after_request
+ def after_admin_request(response):
+ catched.append('after-admin')
+ return response
+ @admin.route('/')
+ def admin_index():
+ return 'the admin'
+ @app.before_request
+ def before_request():
+ catched.append('before-app')
+ @app.after_request
+ def after_request(response):
+ catched.append('after-app')
+ return response
+ @app.route('/')
+ def index():
+ return 'the index'
+ app.register_module(admin)
+ c = app.test_client()
+
+ self.assert_equal(c.get('/').data, 'the index')
+ self.assert_equal(catched, ['before-app', 'after-app'])
+ del catched[:]
+
+ self.assert_equal(c.get('/admin/').data, 'the admin')
+ self.assert_equal(catched, ['before-app', 'before-admin',
+ 'after-admin', 'after-app'])
+
+ @emits_module_deprecation_warning
+ def test_context_processors(self):
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin', url_prefix='/admin')
+ @app.context_processor
+ def inject_all_regualr():
+ return {'a': 1}
+ @admin.context_processor
+ def inject_admin():
+ return {'b': 2}
+ @admin.app_context_processor
+ def inject_all_module():
+ return {'c': 3}
+ @app.route('/')
+ def index():
+ return flask.render_template_string('{{ a }}{{ b }}{{ c }}')
+ @admin.route('/')
+ def admin_index():
+ return flask.render_template_string('{{ a }}{{ b }}{{ c }}')
+ app.register_module(admin)
+ c = app.test_client()
+ self.assert_equal(c.get('/').data, '13')
+ self.assert_equal(c.get('/admin/').data, '123')
+
+ @emits_module_deprecation_warning
+ def test_late_binding(self):
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin')
+ @admin.route('/')
+ def index():
+ return '42'
+ app.register_module(admin, url_prefix='/admin')
+ self.assert_equal(app.test_client().get('/admin/').data, '42')
+
+ @emits_module_deprecation_warning
+ def test_error_handling(self):
+ app = flask.Flask(__name__)
+ admin = flask.Module(__name__, 'admin')
+ @admin.app_errorhandler(404)
+ def not_found(e):
+ return 'not found', 404
+ @admin.app_errorhandler(500)
+ def internal_server_error(e):
+ return 'internal server error', 500
+ @admin.route('/')
+ def index():
+ flask.abort(404)
+ @admin.route('/error')
+ def error():
+ 1 // 0
+ app.register_module(admin)
+ c = app.test_client()
+ rv = c.get('/')
+ self.assert_equal(rv.status_code, 404)
+ self.assert_equal(rv.data, 'not found')
+ rv = c.get('/error')
+ self.assert_equal(rv.status_code, 500)
+ self.assert_equal('internal server error', rv.data)
+
+ def test_templates_and_static(self):
+ app = moduleapp
+ app.testing = True
+ c = app.test_client()
+
+ rv = c.get('/')
+ self.assert_equal(rv.data, 'Hello from the Frontend')
+ rv = c.get('/admin/')
+ self.assert_equal(rv.data, 'Hello from the Admin')
+ rv = c.get('/admin/index2')
+ self.assert_equal(rv.data, 'Hello from the Admin')
+ rv = c.get('/admin/static/test.txt')
+ self.assert_equal(rv.data.strip(), 'Admin File')
+ rv = c.get('/admin/static/css/test.css')
+ self.assert_equal(rv.data.strip(), '/* nested file */')
+
+ with app.test_request_context():
+ self.assert_equal(flask.url_for('admin.static', filename='test.txt'),
+ '/admin/static/test.txt')
+
+ with app.test_request_context():
+ try:
+ flask.render_template('missing.html')
+ except TemplateNotFound, e:
+ self.assert_equal(e.name, 'missing.html')
+ else:
+ self.assert_(0, 'expected exception')
+
+ with flask.Flask(__name__).test_request_context():
+ self.assert_equal(flask.render_template('nested/nested.txt'), 'I\'m nested')
+
+ def test_safe_access(self):
+ app = moduleapp
+
+ with app.test_request_context():
+ f = app.view_functions['admin.static']
+
+ try:
+ f('/etc/passwd')
+ except NotFound:
+ pass
+ else:
+ self.assert_(0, 'expected exception')
+ try:
+ f('../__init__.py')
+ except NotFound:
+ pass
+ else:
+ self.assert_(0, 'expected exception')
+
+ # testcase for a security issue that may exist on windows systems
+ import os
+ import ntpath
+ old_path = os.path
+ os.path = ntpath
+ try:
+ try:
+ f('..\\__init__.py')
+ except NotFound:
+ pass
+ else:
+ self.assert_(0, 'expected exception')
+ finally:
+ os.path = old_path
+
+ @emits_module_deprecation_warning
+ def test_endpoint_decorator(self):
+ from werkzeug.routing import Submount, Rule
+ from flask import Module
+
+ app = flask.Flask(__name__)
+ app.testing = True
+ app.url_map.add(Submount('/foo', [
+ Rule('/bar', endpoint='bar'),
+ Rule('/', endpoint='index')
+ ]))
+ module = Module(__name__, __name__)
+
+ @module.endpoint('bar')
+ def bar():
+ return 'bar'
+
+ @module.endpoint('index')
+ def index():
+ return 'index'
+
+ app.register_module(module)
+
+ c = app.test_client()
+ self.assert_equal(c.get('/foo/').data, 'index')
+ self.assert_equal(c.get('/foo/bar').data, 'bar')
+
+
+class BlueprintTestCase(FlaskTestCase):
+
+ def test_blueprint_specific_error_handling(self):
+ frontend = flask.Blueprint('frontend', __name__)
+ backend = flask.Blueprint('backend', __name__)
+ sideend = flask.Blueprint('sideend', __name__)
+
+ @frontend.errorhandler(403)
+ def frontend_forbidden(e):
+ return 'frontend says no', 403
+
+ @frontend.route('/frontend-no')
+ def frontend_no():
+ flask.abort(403)
+
+ @backend.errorhandler(403)
+ def backend_forbidden(e):
+ return 'backend says no', 403
+
+ @backend.route('/backend-no')
+ def backend_no():
+ flask.abort(403)
+
+ @sideend.route('/what-is-a-sideend')
+ def sideend_no():
+ flask.abort(403)
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(frontend)
+ app.register_blueprint(backend)
+ app.register_blueprint(sideend)
+
+ @app.errorhandler(403)
+ def app_forbidden(e):
+ return 'application itself says no', 403
+
+ c = app.test_client()
+
+ self.assert_equal(c.get('/frontend-no').data, 'frontend says no')
+ self.assert_equal(c.get('/backend-no').data, 'backend says no')
+ self.assert_equal(c.get('/what-is-a-sideend').data, 'application itself says no')
+
+ def test_blueprint_url_definitions(self):
+ bp = flask.Blueprint('test', __name__)
+
+ @bp.route('/foo', defaults={'baz': 42})
+ def foo(bar, baz):
+ return '%s/%d' % (bar, baz)
+
+ @bp.route('/bar')
+ def bar(bar):
+ return unicode(bar)
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(bp, url_prefix='/1', url_defaults={'bar': 23})
+ app.register_blueprint(bp, url_prefix='/2', url_defaults={'bar': 19})
+
+ c = app.test_client()
+ self.assert_equal(c.get('/1/foo').data, u'23/42')
+ self.assert_equal(c.get('/2/foo').data, u'19/42')
+ self.assert_equal(c.get('/1/bar').data, u'23')
+ self.assert_equal(c.get('/2/bar').data, u'19')
+
+ def test_blueprint_url_processors(self):
+ bp = flask.Blueprint('frontend', __name__, url_prefix='/<lang_code>')
+
+ @bp.url_defaults
+ def add_language_code(endpoint, values):
+ values.setdefault('lang_code', flask.g.lang_code)
+
+ @bp.url_value_preprocessor
+ def pull_lang_code(endpoint, values):
+ flask.g.lang_code = values.pop('lang_code')
+
+ @bp.route('/')
+ def index():
+ return flask.url_for('.about')
+
+ @bp.route('/about')
+ def about():
+ return flask.url_for('.index')
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(bp)
+
+ c = app.test_client()
+
+ self.assert_equal(c.get('/de/').data, '/de/about')
+ self.assert_equal(c.get('/de/about').data, '/de/')
+
+ def test_templates_and_static(self):
+ from blueprintapp import app
+ c = app.test_client()
+
+ rv = c.get('/')
+ self.assert_equal(rv.data, 'Hello from the Frontend')
+ rv = c.get('/admin/')
+ self.assert_equal(rv.data, 'Hello from the Admin')
+ rv = c.get('/admin/index2')
+ self.assert_equal(rv.data, 'Hello from the Admin')
+ rv = c.get('/admin/static/test.txt')
+ self.assert_equal(rv.data.strip(), 'Admin File')
+ rv = c.get('/admin/static/css/test.css')
+ self.assert_equal(rv.data.strip(), '/* nested file */')
+
+ with app.test_request_context():
+ self.assert_equal(flask.url_for('admin.static', filename='test.txt'),
+ '/admin/static/test.txt')
+
+ with app.test_request_context():
+ try:
+ flask.render_template('missing.html')
+ except TemplateNotFound, e:
+ self.assert_equal(e.name, 'missing.html')
+ else:
+ self.assert_(0, 'expected exception')
+
+ with flask.Flask(__name__).test_request_context():
+ self.assert_equal(flask.render_template('nested/nested.txt'), 'I\'m nested')
+
+ def test_templates_list(self):
+ from blueprintapp import app
+ templates = sorted(app.jinja_env.list_templates())
+ self.assert_equal(templates, ['admin/index.html',
+ 'frontend/index.html'])
+
+ def test_dotted_names(self):
+ frontend = flask.Blueprint('myapp.frontend', __name__)
+ backend = flask.Blueprint('myapp.backend', __name__)
+
+ @frontend.route('/fe')
+ def frontend_index():
+ return flask.url_for('myapp.backend.backend_index')
+
+ @frontend.route('/fe2')
+ def frontend_page2():
+ return flask.url_for('.frontend_index')
+
+ @backend.route('/be')
+ def backend_index():
+ return flask.url_for('myapp.frontend.frontend_index')
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(frontend)
+ app.register_blueprint(backend)
+
+ c = app.test_client()
+ self.assert_equal(c.get('/fe').data.strip(), '/be')
+ self.assert_equal(c.get('/fe2').data.strip(), '/fe')
+ self.assert_equal(c.get('/be').data.strip(), '/fe')
+
+ def test_empty_url_defaults(self):
+ bp = flask.Blueprint('bp', __name__)
+
+ @bp.route('/', defaults={'page': 1})
+ @bp.route('/page/<int:page>')
+ def something(page):
+ return str(page)
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(bp)
+
+ c = app.test_client()
+ self.assert_equal(c.get('/').data, '1')
+ self.assert_equal(c.get('/page/2').data, '2')
+
+ def test_route_decorator_custom_endpoint(self):
+
+ bp = flask.Blueprint('bp', __name__)
+
+ @bp.route('/foo')
+ def foo():
+ return flask.request.endpoint
+
+ @bp.route('/bar', endpoint='bar')
+ def foo_bar():
+ return flask.request.endpoint
+
+ @bp.route('/bar/123', endpoint='123')
+ def foo_bar_foo():
+ return flask.request.endpoint
+
+ @bp.route('/bar/foo')
+ def bar_foo():
+ return flask.request.endpoint
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(bp, url_prefix='/py')
+
+ @app.route('/')
+ def index():
+ return flask.request.endpoint
+
+ c = app.test_client()
+ self.assertEqual(c.get('/').data, 'index')
+ self.assertEqual(c.get('/py/foo').data, 'bp.foo')
+ self.assertEqual(c.get('/py/bar').data, 'bp.bar')
+ self.assertEqual(c.get('/py/bar/123').data, 'bp.123')
+ self.assertEqual(c.get('/py/bar/foo').data, 'bp.bar_foo')
+
+ def test_route_decorator_custom_endpoint_with_dots(self):
+ bp = flask.Blueprint('bp', __name__)
+
+ @bp.route('/foo')
+ def foo():
+ return flask.request.endpoint
+
+ try:
+ @bp.route('/bar', endpoint='bar.bar')
+ def foo_bar():
+ return flask.request.endpoint
+ except AssertionError:
+ pass
+ else:
+ raise AssertionError('expected AssertionError not raised')
+
+ try:
+ @bp.route('/bar/123', endpoint='bar.123')
+ def foo_bar_foo():
+ return flask.request.endpoint
+ except AssertionError:
+ pass
+ else:
+ raise AssertionError('expected AssertionError not raised')
+
+ def foo_foo_foo():
+ pass
+
+ self.assertRaises(
+ AssertionError,
+ lambda: bp.add_url_rule(
+ '/bar/123', endpoint='bar.123', view_func=foo_foo_foo
+ )
+ )
+
+ self.assertRaises(
+ AssertionError,
+ bp.route('/bar/123', endpoint='bar.123'),
+ lambda: None
+ )
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(bp, url_prefix='/py')
+
+ c = app.test_client()
+ self.assertEqual(c.get('/py/foo').data, 'bp.foo')
+ # The rule's din't actually made it through
+ rv = c.get('/py/bar')
+ assert rv.status_code == 404
+ rv = c.get('/py/bar/123')
+ assert rv.status_code == 404
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(BlueprintTestCase))
+ suite.addTest(unittest.makeSuite(ModuleTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/config.py b/websdk/flask/testsuite/config.py
new file mode 100644
index 0000000..ad1721f
--- /dev/null
+++ b/websdk/flask/testsuite/config.py
@@ -0,0 +1,182 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.config
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Configuration and instances.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+import flask
+import unittest
+from flask.testsuite import FlaskTestCase
+
+
+# config keys used for the ConfigTestCase
+TEST_KEY = 'foo'
+SECRET_KEY = 'devkey'
+
+
+class ConfigTestCase(FlaskTestCase):
+
+ def common_object_test(self, app):
+ self.assert_equal(app.secret_key, 'devkey')
+ self.assert_equal(app.config['TEST_KEY'], 'foo')
+ self.assert_('ConfigTestCase' not in app.config)
+
+ def test_config_from_file(self):
+ app = flask.Flask(__name__)
+ app.config.from_pyfile(__file__.rsplit('.', 1)[0] + '.py')
+ self.common_object_test(app)
+
+ def test_config_from_object(self):
+ app = flask.Flask(__name__)
+ app.config.from_object(__name__)
+ self.common_object_test(app)
+
+ def test_config_from_class(self):
+ class Base(object):
+ TEST_KEY = 'foo'
+ class Test(Base):
+ SECRET_KEY = 'devkey'
+ app = flask.Flask(__name__)
+ app.config.from_object(Test)
+ self.common_object_test(app)
+
+ def test_config_from_envvar(self):
+ env = os.environ
+ try:
+ os.environ = {}
+ app = flask.Flask(__name__)
+ try:
+ app.config.from_envvar('FOO_SETTINGS')
+ except RuntimeError, e:
+ self.assert_("'FOO_SETTINGS' is not set" in str(e))
+ else:
+ self.assert_(0, 'expected exception')
+ self.assert_(not app.config.from_envvar('FOO_SETTINGS', silent=True))
+
+ os.environ = {'FOO_SETTINGS': __file__.rsplit('.', 1)[0] + '.py'}
+ self.assert_(app.config.from_envvar('FOO_SETTINGS'))
+ self.common_object_test(app)
+ finally:
+ os.environ = env
+
+ def test_config_missing(self):
+ app = flask.Flask(__name__)
+ try:
+ app.config.from_pyfile('missing.cfg')
+ except IOError, e:
+ msg = str(e)
+ self.assert_(msg.startswith('[Errno 2] Unable to load configuration '
+ 'file (No such file or directory):'))
+ self.assert_(msg.endswith("missing.cfg'"))
+ else:
+ self.assert_(0, 'expected config')
+ self.assert_(not app.config.from_pyfile('missing.cfg', silent=True))
+
+ def test_session_lifetime(self):
+ app = flask.Flask(__name__)
+ app.config['PERMANENT_SESSION_LIFETIME'] = 42
+ self.assert_equal(app.permanent_session_lifetime.seconds, 42)
+
+
+class InstanceTestCase(FlaskTestCase):
+
+ def test_explicit_instance_paths(self):
+ here = os.path.abspath(os.path.dirname(__file__))
+ try:
+ flask.Flask(__name__, instance_path='instance')
+ except ValueError, e:
+ self.assert_('must be absolute' in str(e))
+ else:
+ self.fail('Expected value error')
+
+ app = flask.Flask(__name__, instance_path=here)
+ self.assert_equal(app.instance_path, here)
+
+ def test_uninstalled_module_paths(self):
+ from config_module_app import app
+ here = os.path.abspath(os.path.dirname(__file__))
+ self.assert_equal(app.instance_path, os.path.join(here, 'test_apps', 'instance'))
+
+ def test_uninstalled_package_paths(self):
+ from config_package_app import app
+ here = os.path.abspath(os.path.dirname(__file__))
+ self.assert_equal(app.instance_path, os.path.join(here, 'test_apps', 'instance'))
+
+ def test_installed_module_paths(self):
+ import types
+ expected_prefix = os.path.abspath('foo')
+ mod = types.ModuleType('myapp')
+ mod.__file__ = os.path.join(expected_prefix, 'lib', 'python2.5',
+ 'site-packages', 'myapp.py')
+ sys.modules['myapp'] = mod
+ try:
+ mod.app = flask.Flask(mod.__name__)
+ self.assert_equal(mod.app.instance_path,
+ os.path.join(expected_prefix, 'var',
+ 'myapp-instance'))
+ finally:
+ sys.modules['myapp'] = None
+
+ def test_installed_package_paths(self):
+ import types
+ expected_prefix = os.path.abspath('foo')
+ package_path = os.path.join(expected_prefix, 'lib', 'python2.5',
+ 'site-packages', 'myapp')
+ mod = types.ModuleType('myapp')
+ mod.__path__ = [package_path]
+ mod.__file__ = os.path.join(package_path, '__init__.py')
+ sys.modules['myapp'] = mod
+ try:
+ mod.app = flask.Flask(mod.__name__)
+ self.assert_equal(mod.app.instance_path,
+ os.path.join(expected_prefix, 'var',
+ 'myapp-instance'))
+ finally:
+ sys.modules['myapp'] = None
+
+ def test_prefix_installed_paths(self):
+ import types
+ expected_prefix = os.path.abspath(sys.prefix)
+ package_path = os.path.join(expected_prefix, 'lib', 'python2.5',
+ 'site-packages', 'myapp')
+ mod = types.ModuleType('myapp')
+ mod.__path__ = [package_path]
+ mod.__file__ = os.path.join(package_path, '__init__.py')
+ sys.modules['myapp'] = mod
+ try:
+ mod.app = flask.Flask(mod.__name__)
+ self.assert_equal(mod.app.instance_path,
+ os.path.join(expected_prefix, 'var',
+ 'myapp-instance'))
+ finally:
+ sys.modules['myapp'] = None
+
+ def test_egg_installed_paths(self):
+ import types
+ expected_prefix = os.path.abspath(sys.prefix)
+ package_path = os.path.join(expected_prefix, 'lib', 'python2.5',
+ 'site-packages', 'MyApp.egg', 'myapp')
+ mod = types.ModuleType('myapp')
+ mod.__path__ = [package_path]
+ mod.__file__ = os.path.join(package_path, '__init__.py')
+ sys.modules['myapp'] = mod
+ try:
+ mod.app = flask.Flask(mod.__name__)
+ self.assert_equal(mod.app.instance_path,
+ os.path.join(expected_prefix, 'var',
+ 'myapp-instance'))
+ finally:
+ sys.modules['myapp'] = None
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ConfigTestCase))
+ suite.addTest(unittest.makeSuite(InstanceTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/deprecations.py b/websdk/flask/testsuite/deprecations.py
new file mode 100644
index 0000000..795a5d3
--- /dev/null
+++ b/websdk/flask/testsuite/deprecations.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.deprecations
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests deprecation support.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import flask
+import unittest
+from flask.testsuite import FlaskTestCase, catch_warnings
+
+
+class DeprecationsTestCase(FlaskTestCase):
+
+ def test_init_jinja_globals(self):
+ class MyFlask(flask.Flask):
+ def init_jinja_globals(self):
+ self.jinja_env.globals['foo'] = '42'
+
+ with catch_warnings() as log:
+ app = MyFlask(__name__)
+ @app.route('/')
+ def foo():
+ return app.jinja_env.globals['foo']
+
+ c = app.test_client()
+ self.assert_equal(c.get('/').data, '42')
+ self.assert_equal(len(log), 1)
+ self.assert_('init_jinja_globals' in str(log[0]['message']))
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(DeprecationsTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/examples.py b/websdk/flask/testsuite/examples.py
new file mode 100644
index 0000000..2d30958
--- /dev/null
+++ b/websdk/flask/testsuite/examples.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.examples
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the examples.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import unittest
+from flask.testsuite import add_to_path
+
+
+def setup_path():
+ example_path = os.path.join(os.path.dirname(__file__),
+ os.pardir, os.pardir, 'examples')
+ add_to_path(os.path.join(example_path, 'flaskr'))
+ add_to_path(os.path.join(example_path, 'minitwit'))
+
+
+def suite():
+ setup_path()
+ suite = unittest.TestSuite()
+ try:
+ from minitwit_tests import MiniTwitTestCase
+ except ImportError:
+ pass
+ else:
+ suite.addTest(unittest.makeSuite(MiniTwitTestCase))
+ try:
+ from flaskr_tests import FlaskrTestCase
+ except ImportError:
+ pass
+ else:
+ suite.addTest(unittest.makeSuite(FlaskrTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/ext.py b/websdk/flask/testsuite/ext.py
new file mode 100644
index 0000000..034ab5b
--- /dev/null
+++ b/websdk/flask/testsuite/ext.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.ext
+ ~~~~~~~~~~~~~~~~~~~
+
+ Tests the extension import thing.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from __future__ import with_statement
+
+import sys
+import unittest
+from flask.testsuite import FlaskTestCase
+
+
+class ExtImportHookTestCase(FlaskTestCase):
+
+ def setup(self):
+ # we clear this out for various reasons. The most important one is
+ # that a real flaskext could be in there which would disable our
+ # fake package. Secondly we want to make sure that the flaskext
+ # import hook does not break on reloading.
+ for entry, value in sys.modules.items():
+ if (entry.startswith('flask.ext.') or
+ entry.startswith('flask_') or
+ entry.startswith('flaskext.') or
+ entry == 'flaskext') and value is not None:
+ sys.modules.pop(entry, None)
+ from flask import ext
+ reload(ext)
+
+ # reloading must not add more hooks
+ import_hooks = 0
+ for item in sys.meta_path:
+ cls = type(item)
+ if cls.__module__ == 'flask.exthook' and \
+ cls.__name__ == 'ExtensionImporter':
+ import_hooks += 1
+ self.assert_equal(import_hooks, 1)
+
+ def teardown(self):
+ from flask import ext
+ for key in ext.__dict__:
+ self.assert_('.' not in key)
+
+ def test_flaskext_new_simple_import_normal(self):
+ from flask.ext.newext_simple import ext_id
+ self.assert_equal(ext_id, 'newext_simple')
+
+ def test_flaskext_new_simple_import_module(self):
+ from flask.ext import newext_simple
+ self.assert_equal(newext_simple.ext_id, 'newext_simple')
+ self.assert_equal(newext_simple.__name__, 'flask_newext_simple')
+
+ def test_flaskext_new_package_import_normal(self):
+ from flask.ext.newext_package import ext_id
+ self.assert_equal(ext_id, 'newext_package')
+
+ def test_flaskext_new_package_import_module(self):
+ from flask.ext import newext_package
+ self.assert_equal(newext_package.ext_id, 'newext_package')
+ self.assert_equal(newext_package.__name__, 'flask_newext_package')
+
+ def test_flaskext_new_package_import_submodule_function(self):
+ from flask.ext.newext_package.submodule import test_function
+ self.assert_equal(test_function(), 42)
+
+ def test_flaskext_new_package_import_submodule(self):
+ from flask.ext.newext_package import submodule
+ self.assert_equal(submodule.__name__, 'flask_newext_package.submodule')
+ self.assert_equal(submodule.test_function(), 42)
+
+ def test_flaskext_old_simple_import_normal(self):
+ from flask.ext.oldext_simple import ext_id
+ self.assert_equal(ext_id, 'oldext_simple')
+
+ def test_flaskext_old_simple_import_module(self):
+ from flask.ext import oldext_simple
+ self.assert_equal(oldext_simple.ext_id, 'oldext_simple')
+ self.assert_equal(oldext_simple.__name__, 'flaskext.oldext_simple')
+
+ def test_flaskext_old_package_import_normal(self):
+ from flask.ext.oldext_package import ext_id
+ self.assert_equal(ext_id, 'oldext_package')
+
+ def test_flaskext_old_package_import_module(self):
+ from flask.ext import oldext_package
+ self.assert_equal(oldext_package.ext_id, 'oldext_package')
+ self.assert_equal(oldext_package.__name__, 'flaskext.oldext_package')
+
+ def test_flaskext_old_package_import_submodule(self):
+ from flask.ext.oldext_package import submodule
+ self.assert_equal(submodule.__name__, 'flaskext.oldext_package.submodule')
+ self.assert_equal(submodule.test_function(), 42)
+
+ def test_flaskext_old_package_import_submodule_function(self):
+ from flask.ext.oldext_package.submodule import test_function
+ self.assert_equal(test_function(), 42)
+
+ def test_flaskext_broken_package_no_module_caching(self):
+ for x in xrange(2):
+ with self.assert_raises(ImportError):
+ import flask.ext.broken
+
+ def test_no_error_swallowing(self):
+ try:
+ import flask.ext.broken
+ except ImportError:
+ exc_type, exc_value, tb = sys.exc_info()
+ self.assert_(exc_type is ImportError)
+ self.assert_equal(str(exc_value), 'No module named missing_module')
+ self.assert_(tb.tb_frame.f_globals is globals())
+
+ next = tb.tb_next
+ self.assert_('flask_broken/__init__.py' in next.tb_frame.f_code.co_filename)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ExtImportHookTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/helpers.py b/websdk/flask/testsuite/helpers.py
new file mode 100644
index 0000000..052d36e
--- /dev/null
+++ b/websdk/flask/testsuite/helpers.py
@@ -0,0 +1,298 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.helpers
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Various helpers.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import os
+import flask
+import unittest
+from logging import StreamHandler
+from StringIO import StringIO
+from flask.testsuite import FlaskTestCase, catch_warnings, catch_stderr
+from werkzeug.http import parse_options_header
+
+
+def has_encoding(name):
+ try:
+ import codecs
+ codecs.lookup(name)
+ return True
+ except LookupError:
+ return False
+
+
+class JSONTestCase(FlaskTestCase):
+
+ def test_json_bad_requests(self):
+ app = flask.Flask(__name__)
+ @app.route('/json', methods=['POST'])
+ def return_json():
+ return unicode(flask.request.json)
+ c = app.test_client()
+ rv = c.post('/json', data='malformed', content_type='application/json')
+ self.assert_equal(rv.status_code, 400)
+
+ def test_json_body_encoding(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ @app.route('/')
+ def index():
+ return flask.request.json
+
+ c = app.test_client()
+ resp = c.get('/', data=u'"Hällo Wörld"'.encode('iso-8859-15'),
+ content_type='application/json; charset=iso-8859-15')
+ self.assert_equal(resp.data, u'Hällo Wörld'.encode('utf-8'))
+
+ def test_jsonify(self):
+ d = dict(a=23, b=42, c=[1, 2, 3])
+ app = flask.Flask(__name__)
+ @app.route('/kw')
+ def return_kwargs():
+ return flask.jsonify(**d)
+ @app.route('/dict')
+ def return_dict():
+ return flask.jsonify(d)
+ c = app.test_client()
+ for url in '/kw', '/dict':
+ rv = c.get(url)
+ self.assert_equal(rv.mimetype, 'application/json')
+ self.assert_equal(flask.json.loads(rv.data), d)
+
+ def test_json_attr(self):
+ app = flask.Flask(__name__)
+ @app.route('/add', methods=['POST'])
+ def add():
+ return unicode(flask.request.json['a'] + flask.request.json['b'])
+ c = app.test_client()
+ rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}),
+ content_type='application/json')
+ self.assert_equal(rv.data, '3')
+
+ def test_template_escaping(self):
+ app = flask.Flask(__name__)
+ render = flask.render_template_string
+ with app.test_request_context():
+ rv = render('{{ "</script>"|tojson|safe }}')
+ self.assert_equal(rv, '"<\\/script>"')
+ rv = render('{{ "<\0/script>"|tojson|safe }}')
+ self.assert_equal(rv, '"<\\u0000\\/script>"')
+
+ def test_modified_url_encoding(self):
+ class ModifiedRequest(flask.Request):
+ url_charset = 'euc-kr'
+ app = flask.Flask(__name__)
+ app.request_class = ModifiedRequest
+ app.url_map.charset = 'euc-kr'
+
+ @app.route('/')
+ def index():
+ return flask.request.args['foo']
+
+ rv = app.test_client().get(u'/?foo=정상처리'.encode('euc-kr'))
+ self.assert_equal(rv.status_code, 200)
+ self.assert_equal(rv.data, u'정상처리'.encode('utf-8'))
+
+ if not has_encoding('euc-kr'):
+ test_modified_url_encoding = None
+
+
+class SendfileTestCase(FlaskTestCase):
+
+ def test_send_file_regular(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ rv = flask.send_file('static/index.html')
+ self.assert_(rv.direct_passthrough)
+ self.assert_equal(rv.mimetype, 'text/html')
+ with app.open_resource('static/index.html') as f:
+ self.assert_equal(rv.data, f.read())
+
+ def test_send_file_xsendfile(self):
+ app = flask.Flask(__name__)
+ app.use_x_sendfile = True
+ with app.test_request_context():
+ rv = flask.send_file('static/index.html')
+ self.assert_(rv.direct_passthrough)
+ self.assert_('x-sendfile' in rv.headers)
+ self.assert_equal(rv.headers['x-sendfile'],
+ os.path.join(app.root_path, 'static/index.html'))
+ self.assert_equal(rv.mimetype, 'text/html')
+
+ def test_send_file_object(self):
+ app = flask.Flask(__name__)
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = open(os.path.join(app.root_path, 'static/index.html'))
+ rv = flask.send_file(f)
+ with app.open_resource('static/index.html') as f:
+ self.assert_equal(rv.data, f.read())
+ self.assert_equal(rv.mimetype, 'text/html')
+ # mimetypes + etag
+ self.assert_equal(len(captured), 2)
+
+ app.use_x_sendfile = True
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = open(os.path.join(app.root_path, 'static/index.html'))
+ rv = flask.send_file(f)
+ self.assert_equal(rv.mimetype, 'text/html')
+ self.assert_('x-sendfile' in rv.headers)
+ self.assert_equal(rv.headers['x-sendfile'],
+ os.path.join(app.root_path, 'static/index.html'))
+ # mimetypes + etag
+ self.assert_equal(len(captured), 2)
+
+ app.use_x_sendfile = False
+ with app.test_request_context():
+ with catch_warnings() as captured:
+ f = StringIO('Test')
+ rv = flask.send_file(f)
+ self.assert_equal(rv.data, 'Test')
+ self.assert_equal(rv.mimetype, 'application/octet-stream')
+ # etags
+ self.assert_equal(len(captured), 1)
+ with catch_warnings() as captured:
+ f = StringIO('Test')
+ rv = flask.send_file(f, mimetype='text/plain')
+ self.assert_equal(rv.data, 'Test')
+ self.assert_equal(rv.mimetype, 'text/plain')
+ # etags
+ self.assert_equal(len(captured), 1)
+
+ app.use_x_sendfile = True
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = StringIO('Test')
+ rv = flask.send_file(f)
+ self.assert_('x-sendfile' not in rv.headers)
+ # etags
+ self.assert_equal(len(captured), 1)
+
+ def test_attachment(self):
+ app = flask.Flask(__name__)
+ with catch_warnings() as captured:
+ with app.test_request_context():
+ f = open(os.path.join(app.root_path, 'static/index.html'))
+ rv = flask.send_file(f, as_attachment=True)
+ value, options = parse_options_header(rv.headers['Content-Disposition'])
+ self.assert_equal(value, 'attachment')
+ # mimetypes + etag
+ self.assert_equal(len(captured), 2)
+
+ with app.test_request_context():
+ self.assert_equal(options['filename'], 'index.html')
+ rv = flask.send_file('static/index.html', as_attachment=True)
+ value, options = parse_options_header(rv.headers['Content-Disposition'])
+ self.assert_equal(value, 'attachment')
+ self.assert_equal(options['filename'], 'index.html')
+
+ with app.test_request_context():
+ rv = flask.send_file(StringIO('Test'), as_attachment=True,
+ attachment_filename='index.txt',
+ add_etags=False)
+ self.assert_equal(rv.mimetype, 'text/plain')
+ value, options = parse_options_header(rv.headers['Content-Disposition'])
+ self.assert_equal(value, 'attachment')
+ self.assert_equal(options['filename'], 'index.txt')
+
+
+class LoggingTestCase(FlaskTestCase):
+
+ def test_logger_cache(self):
+ app = flask.Flask(__name__)
+ logger1 = app.logger
+ self.assert_(app.logger is logger1)
+ self.assert_equal(logger1.name, __name__)
+ app.logger_name = __name__ + '/test_logger_cache'
+ self.assert_(app.logger is not logger1)
+
+ def test_debug_log(self):
+ app = flask.Flask(__name__)
+ app.debug = True
+
+ @app.route('/')
+ def index():
+ app.logger.warning('the standard library is dead')
+ app.logger.debug('this is a debug statement')
+ return ''
+
+ @app.route('/exc')
+ def exc():
+ 1/0
+
+ with app.test_client() as c:
+ with catch_stderr() as err:
+ c.get('/')
+ out = err.getvalue()
+ self.assert_('WARNING in helpers [' in out)
+ self.assert_(os.path.basename(__file__.rsplit('.', 1)[0] + '.py') in out)
+ self.assert_('the standard library is dead' in out)
+ self.assert_('this is a debug statement' in out)
+
+ with catch_stderr() as err:
+ try:
+ c.get('/exc')
+ except ZeroDivisionError:
+ pass
+ else:
+ self.assert_(False, 'debug log ate the exception')
+
+ def test_exception_logging(self):
+ out = StringIO()
+ app = flask.Flask(__name__)
+ app.logger_name = 'flask_tests/test_exception_logging'
+ app.logger.addHandler(StreamHandler(out))
+
+ @app.route('/')
+ def index():
+ 1/0
+
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.status_code, 500)
+ self.assert_('Internal Server Error' in rv.data)
+
+ err = out.getvalue()
+ self.assert_('Exception on / [GET]' in err)
+ self.assert_('Traceback (most recent call last):' in err)
+ self.assert_('1/0' in err)
+ self.assert_('ZeroDivisionError:' in err)
+
+ def test_processor_exceptions(self):
+ app = flask.Flask(__name__)
+ @app.before_request
+ def before_request():
+ if trigger == 'before':
+ 1/0
+ @app.after_request
+ def after_request(response):
+ if trigger == 'after':
+ 1/0
+ return response
+ @app.route('/')
+ def index():
+ return 'Foo'
+ @app.errorhandler(500)
+ def internal_server_error(e):
+ return 'Hello Server Error', 500
+ for trigger in 'before', 'after':
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.status_code, 500)
+ self.assert_equal(rv.data, 'Hello Server Error')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ if flask.json_available:
+ suite.addTest(unittest.makeSuite(JSONTestCase))
+ suite.addTest(unittest.makeSuite(SendfileTestCase))
+ suite.addTest(unittest.makeSuite(LoggingTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/signals.py b/websdk/flask/testsuite/signals.py
new file mode 100644
index 0000000..da1a68c
--- /dev/null
+++ b/websdk/flask/testsuite/signals.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.signals
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Signalling.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import flask
+import unittest
+from flask.testsuite import FlaskTestCase
+
+
+class SignalsTestCase(FlaskTestCase):
+
+ def test_template_rendered(self):
+ app = flask.Flask(__name__)
+
+ @app.route('/')
+ def index():
+ return flask.render_template('simple_template.html', whiskey=42)
+
+ recorded = []
+ def record(sender, template, context):
+ recorded.append((template, context))
+
+ flask.template_rendered.connect(record, app)
+ try:
+ app.test_client().get('/')
+ self.assert_equal(len(recorded), 1)
+ template, context = recorded[0]
+ self.assert_equal(template.name, 'simple_template.html')
+ self.assert_equal(context['whiskey'], 42)
+ finally:
+ flask.template_rendered.disconnect(record, app)
+
+ def test_request_signals(self):
+ app = flask.Flask(__name__)
+ calls = []
+
+ def before_request_signal(sender):
+ calls.append('before-signal')
+
+ def after_request_signal(sender, response):
+ self.assert_equal(response.data, 'stuff')
+ calls.append('after-signal')
+
+ @app.before_request
+ def before_request_handler():
+ calls.append('before-handler')
+
+ @app.after_request
+ def after_request_handler(response):
+ calls.append('after-handler')
+ response.data = 'stuff'
+ return response
+
+ @app.route('/')
+ def index():
+ calls.append('handler')
+ return 'ignored anyway'
+
+ flask.request_started.connect(before_request_signal, app)
+ flask.request_finished.connect(after_request_signal, app)
+
+ try:
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, 'stuff')
+
+ self.assert_equal(calls, ['before-signal', 'before-handler',
+ 'handler', 'after-handler',
+ 'after-signal'])
+ finally:
+ flask.request_started.disconnect(before_request_signal, app)
+ flask.request_finished.disconnect(after_request_signal, app)
+
+ def test_request_exception_signal(self):
+ app = flask.Flask(__name__)
+ recorded = []
+
+ @app.route('/')
+ def index():
+ 1/0
+
+ def record(sender, exception):
+ recorded.append(exception)
+
+ flask.got_request_exception.connect(record, app)
+ try:
+ self.assert_equal(app.test_client().get('/').status_code, 500)
+ self.assert_equal(len(recorded), 1)
+ self.assert_(isinstance(recorded[0], ZeroDivisionError))
+ finally:
+ flask.got_request_exception.disconnect(record, app)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ if flask.signals_available:
+ suite.addTest(unittest.makeSuite(SignalsTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/static/index.html b/websdk/flask/testsuite/static/index.html
new file mode 100644
index 0000000..de8b69b
--- /dev/null
+++ b/websdk/flask/testsuite/static/index.html
@@ -0,0 +1 @@
+<h1>Hello World!</h1>
diff --git a/websdk/flask/testsuite/subclassing.py b/websdk/flask/testsuite/subclassing.py
new file mode 100644
index 0000000..e56ad56
--- /dev/null
+++ b/websdk/flask/testsuite/subclassing.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.subclassing
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Test that certain behavior of flask can be customized by
+ subclasses.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import flask
+import unittest
+from StringIO import StringIO
+from logging import StreamHandler
+from flask.testsuite import FlaskTestCase
+
+
+class FlaskSubclassingTestCase(FlaskTestCase):
+
+ def test_supressed_exception_logging(self):
+ class SupressedFlask(flask.Flask):
+ def log_exception(self, exc_info):
+ pass
+
+ out = StringIO()
+ app = SupressedFlask(__name__)
+ app.logger_name = 'flask_tests/test_supressed_exception_logging'
+ app.logger.addHandler(StreamHandler(out))
+
+ @app.route('/')
+ def index():
+ 1/0
+
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.status_code, 500)
+ self.assert_('Internal Server Error' in rv.data)
+
+ err = out.getvalue()
+ self.assert_equal(err, '')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(FlaskSubclassingTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/templates/_macro.html b/websdk/flask/testsuite/templates/_macro.html
new file mode 100644
index 0000000..3460ae2
--- /dev/null
+++ b/websdk/flask/testsuite/templates/_macro.html
@@ -0,0 +1 @@
+{% macro hello(name) %}Hello {{ name }}!{% endmacro %}
diff --git a/websdk/flask/testsuite/templates/context_template.html b/websdk/flask/testsuite/templates/context_template.html
new file mode 100644
index 0000000..fadf3e5
--- /dev/null
+++ b/websdk/flask/testsuite/templates/context_template.html
@@ -0,0 +1 @@
+<p>{{ value }}|{{ injected_value }}
diff --git a/websdk/flask/testsuite/templates/escaping_template.html b/websdk/flask/testsuite/templates/escaping_template.html
new file mode 100644
index 0000000..dc47644
--- /dev/null
+++ b/websdk/flask/testsuite/templates/escaping_template.html
@@ -0,0 +1,6 @@
+{{ text }}
+{{ html }}
+{% autoescape false %}{{ text }}
+{{ html }}{% endautoescape %}
+{% autoescape true %}{{ text }}
+{{ html }}{% endautoescape %}
diff --git a/websdk/flask/testsuite/templates/mail.txt b/websdk/flask/testsuite/templates/mail.txt
new file mode 100644
index 0000000..d6cb92e
--- /dev/null
+++ b/websdk/flask/testsuite/templates/mail.txt
@@ -0,0 +1 @@
+{{ foo}} Mail
diff --git a/websdk/flask/testsuite/templates/nested/nested.txt b/websdk/flask/testsuite/templates/nested/nested.txt
new file mode 100644
index 0000000..2c8634f
--- /dev/null
+++ b/websdk/flask/testsuite/templates/nested/nested.txt
@@ -0,0 +1 @@
+I'm nested
diff --git a/websdk/flask/testsuite/templates/simple_template.html b/websdk/flask/testsuite/templates/simple_template.html
new file mode 100644
index 0000000..c24612c
--- /dev/null
+++ b/websdk/flask/testsuite/templates/simple_template.html
@@ -0,0 +1 @@
+<h1>{{ whiskey }}</h1>
diff --git a/websdk/flask/testsuite/templates/template_filter.html b/websdk/flask/testsuite/templates/template_filter.html
new file mode 100644
index 0000000..af46cd9
--- /dev/null
+++ b/websdk/flask/testsuite/templates/template_filter.html
@@ -0,0 +1 @@
+{{ value|super_reverse }} \ No newline at end of file
diff --git a/websdk/flask/testsuite/templating.py b/websdk/flask/testsuite/templating.py
new file mode 100644
index 0000000..453bfb6
--- /dev/null
+++ b/websdk/flask/testsuite/templating.py
@@ -0,0 +1,144 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.templating
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Template functionality
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import flask
+import unittest
+from flask.testsuite import FlaskTestCase
+
+
+class TemplatingTestCase(FlaskTestCase):
+
+ def test_context_processing(self):
+ app = flask.Flask(__name__)
+ @app.context_processor
+ def context_processor():
+ return {'injected_value': 42}
+ @app.route('/')
+ def index():
+ return flask.render_template('context_template.html', value=23)
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, '<p>23|42')
+
+ def test_original_win(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return flask.render_template_string('{{ config }}', config=42)
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, '42')
+
+ def test_standard_context(self):
+ app = flask.Flask(__name__)
+ app.secret_key = 'development key'
+ @app.route('/')
+ def index():
+ flask.g.foo = 23
+ flask.session['test'] = 'aha'
+ return flask.render_template_string('''
+ {{ request.args.foo }}
+ {{ g.foo }}
+ {{ config.DEBUG }}
+ {{ session.test }}
+ ''')
+ rv = app.test_client().get('/?foo=42')
+ self.assert_equal(rv.data.split(), ['42', '23', 'False', 'aha'])
+
+ def test_escaping(self):
+ text = '<p>Hello World!'
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ return flask.render_template('escaping_template.html', text=text,
+ html=flask.Markup(text))
+ lines = app.test_client().get('/').data.splitlines()
+ self.assert_equal(lines, [
+ '&lt;p&gt;Hello World!',
+ '<p>Hello World!',
+ '<p>Hello World!',
+ '<p>Hello World!',
+ '&lt;p&gt;Hello World!',
+ '<p>Hello World!'
+ ])
+
+ def test_no_escaping(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ self.assert_equal(flask.render_template_string('{{ foo }}',
+ foo='<test>'), '<test>')
+ self.assert_equal(flask.render_template('mail.txt', foo='<test>'),
+ '<test> Mail')
+
+ def test_macros(self):
+ app = flask.Flask(__name__)
+ with app.test_request_context():
+ macro = flask.get_template_attribute('_macro.html', 'hello')
+ self.assert_equal(macro('World'), 'Hello World!')
+
+ def test_template_filter(self):
+ app = flask.Flask(__name__)
+ @app.template_filter()
+ def my_reverse(s):
+ return s[::-1]
+ self.assert_('my_reverse' in app.jinja_env.filters.keys())
+ self.assert_equal(app.jinja_env.filters['my_reverse'], my_reverse)
+ self.assert_equal(app.jinja_env.filters['my_reverse']('abcd'), 'dcba')
+
+ def test_template_filter_with_name(self):
+ app = flask.Flask(__name__)
+ @app.template_filter('strrev')
+ def my_reverse(s):
+ return s[::-1]
+ self.assert_('strrev' in app.jinja_env.filters.keys())
+ self.assert_equal(app.jinja_env.filters['strrev'], my_reverse)
+ self.assert_equal(app.jinja_env.filters['strrev']('abcd'), 'dcba')
+
+ def test_template_filter_with_template(self):
+ app = flask.Flask(__name__)
+ @app.template_filter()
+ def super_reverse(s):
+ return s[::-1]
+ @app.route('/')
+ def index():
+ return flask.render_template('template_filter.html', value='abcd')
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, 'dcba')
+
+ def test_template_filter_with_name_and_template(self):
+ app = flask.Flask(__name__)
+ @app.template_filter('super_reverse')
+ def my_reverse(s):
+ return s[::-1]
+ @app.route('/')
+ def index():
+ return flask.render_template('template_filter.html', value='abcd')
+ rv = app.test_client().get('/')
+ self.assert_equal(rv.data, 'dcba')
+
+ def test_custom_template_loader(self):
+ class MyFlask(flask.Flask):
+ def create_global_jinja_loader(self):
+ from jinja2 import DictLoader
+ return DictLoader({'index.html': 'Hello Custom World!'})
+ app = MyFlask(__name__)
+ @app.route('/')
+ def index():
+ return flask.render_template('index.html')
+ c = app.test_client()
+ rv = c.get('/')
+ self.assert_equal(rv.data, 'Hello Custom World!')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TemplatingTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/test_apps/blueprintapp/__init__.py b/websdk/flask/testsuite/test_apps/blueprintapp/__init__.py
new file mode 100644
index 0000000..2b8ef75
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/blueprintapp/__init__.py
@@ -0,0 +1,7 @@
+from flask import Flask
+
+app = Flask(__name__)
+from blueprintapp.apps.admin import admin
+from blueprintapp.apps.frontend import frontend
+app.register_blueprint(admin)
+app.register_blueprint(frontend)
diff --git a/websdk/flask/testsuite/test_apps/blueprintapp/apps/__init__.py b/websdk/flask/testsuite/test_apps/blueprintapp/apps/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/blueprintapp/apps/__init__.py
diff --git a/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/__init__.py b/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/__init__.py
new file mode 100644
index 0000000..3f714d9
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/__init__.py
@@ -0,0 +1,15 @@
+from flask import Blueprint, render_template
+
+admin = Blueprint('admin', __name__, url_prefix='/admin',
+ template_folder='templates',
+ static_folder='static')
+
+
+@admin.route('/')
+def index():
+ return render_template('admin/index.html')
+
+
+@admin.route('/index2')
+def index2():
+ return render_template('./admin/index.html')
diff --git a/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/static/css/test.css b/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/static/css/test.css
new file mode 100644
index 0000000..b9f564d
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/static/css/test.css
@@ -0,0 +1 @@
+/* nested file */
diff --git a/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/static/test.txt b/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/static/test.txt
new file mode 100644
index 0000000..f220d22
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/static/test.txt
@@ -0,0 +1 @@
+Admin File
diff --git a/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/templates/admin/index.html b/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/templates/admin/index.html
new file mode 100644
index 0000000..eeec199
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/blueprintapp/apps/admin/templates/admin/index.html
@@ -0,0 +1 @@
+Hello from the Admin
diff --git a/websdk/flask/testsuite/test_apps/blueprintapp/apps/frontend/__init__.py b/websdk/flask/testsuite/test_apps/blueprintapp/apps/frontend/__init__.py
new file mode 100644
index 0000000..69c8666
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/blueprintapp/apps/frontend/__init__.py
@@ -0,0 +1,8 @@
+from flask import Blueprint, render_template
+
+frontend = Blueprint('frontend', __name__, template_folder='templates')
+
+
+@frontend.route('/')
+def index():
+ return render_template('frontend/index.html')
diff --git a/websdk/flask/testsuite/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html b/websdk/flask/testsuite/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html
new file mode 100644
index 0000000..a062d71
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html
@@ -0,0 +1 @@
+Hello from the Frontend
diff --git a/websdk/flask/testsuite/test_apps/config_module_app.py b/websdk/flask/testsuite/test_apps/config_module_app.py
new file mode 100644
index 0000000..380d46b
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/config_module_app.py
@@ -0,0 +1,4 @@
+import os
+import flask
+here = os.path.abspath(os.path.dirname(__file__))
+app = flask.Flask(__name__)
diff --git a/websdk/flask/testsuite/test_apps/config_package_app/__init__.py b/websdk/flask/testsuite/test_apps/config_package_app/__init__.py
new file mode 100644
index 0000000..380d46b
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/config_package_app/__init__.py
@@ -0,0 +1,4 @@
+import os
+import flask
+here = os.path.abspath(os.path.dirname(__file__))
+app = flask.Flask(__name__)
diff --git a/websdk/flask/testsuite/test_apps/flask_broken/__init__.py b/websdk/flask/testsuite/test_apps/flask_broken/__init__.py
new file mode 100644
index 0000000..c194c04
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/flask_broken/__init__.py
@@ -0,0 +1,2 @@
+import flask.ext.broken.b
+import missing_module
diff --git a/websdk/flask/testsuite/test_apps/flask_broken/b.py b/websdk/flask/testsuite/test_apps/flask_broken/b.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/flask_broken/b.py
diff --git a/websdk/flask/testsuite/test_apps/flask_newext_package/__init__.py b/websdk/flask/testsuite/test_apps/flask_newext_package/__init__.py
new file mode 100644
index 0000000..3fd13e1
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/flask_newext_package/__init__.py
@@ -0,0 +1 @@
+ext_id = 'newext_package'
diff --git a/websdk/flask/testsuite/test_apps/flask_newext_package/submodule.py b/websdk/flask/testsuite/test_apps/flask_newext_package/submodule.py
new file mode 100644
index 0000000..26ad56b
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/flask_newext_package/submodule.py
@@ -0,0 +1,2 @@
+def test_function():
+ return 42
diff --git a/websdk/flask/testsuite/test_apps/flask_newext_simple.py b/websdk/flask/testsuite/test_apps/flask_newext_simple.py
new file mode 100644
index 0000000..dc4a362
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/flask_newext_simple.py
@@ -0,0 +1 @@
+ext_id = 'newext_simple'
diff --git a/websdk/flask/testsuite/test_apps/flaskext/__init__.py b/websdk/flask/testsuite/test_apps/flaskext/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/flaskext/__init__.py
diff --git a/websdk/flask/testsuite/test_apps/flaskext/oldext_package/__init__.py b/websdk/flask/testsuite/test_apps/flaskext/oldext_package/__init__.py
new file mode 100644
index 0000000..7c46206
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/flaskext/oldext_package/__init__.py
@@ -0,0 +1 @@
+ext_id = 'oldext_package'
diff --git a/websdk/flask/testsuite/test_apps/flaskext/oldext_package/submodule.py b/websdk/flask/testsuite/test_apps/flaskext/oldext_package/submodule.py
new file mode 100644
index 0000000..26ad56b
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/flaskext/oldext_package/submodule.py
@@ -0,0 +1,2 @@
+def test_function():
+ return 42
diff --git a/websdk/flask/testsuite/test_apps/flaskext/oldext_simple.py b/websdk/flask/testsuite/test_apps/flaskext/oldext_simple.py
new file mode 100644
index 0000000..c6664a7
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/flaskext/oldext_simple.py
@@ -0,0 +1 @@
+ext_id = 'oldext_simple'
diff --git a/websdk/flask/testsuite/test_apps/moduleapp/__init__.py b/websdk/flask/testsuite/test_apps/moduleapp/__init__.py
new file mode 100644
index 0000000..35e82d4
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/moduleapp/__init__.py
@@ -0,0 +1,7 @@
+from flask import Flask
+
+app = Flask(__name__)
+from moduleapp.apps.admin import admin
+from moduleapp.apps.frontend import frontend
+app.register_module(admin)
+app.register_module(frontend)
diff --git a/websdk/flask/testsuite/test_apps/moduleapp/apps/__init__.py b/websdk/flask/testsuite/test_apps/moduleapp/apps/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/moduleapp/apps/__init__.py
diff --git a/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/__init__.py b/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/__init__.py
new file mode 100644
index 0000000..b85b802
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/__init__.py
@@ -0,0 +1,14 @@
+from flask import Module, render_template
+
+
+admin = Module(__name__, url_prefix='/admin')
+
+
+@admin.route('/')
+def index():
+ return render_template('admin/index.html')
+
+
+@admin.route('/index2')
+def index2():
+ return render_template('./admin/index.html')
diff --git a/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/static/css/test.css b/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/static/css/test.css
new file mode 100644
index 0000000..b9f564d
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/static/css/test.css
@@ -0,0 +1 @@
+/* nested file */
diff --git a/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/static/test.txt b/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/static/test.txt
new file mode 100644
index 0000000..f220d22
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/static/test.txt
@@ -0,0 +1 @@
+Admin File
diff --git a/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/templates/index.html b/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/templates/index.html
new file mode 100644
index 0000000..eeec199
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/moduleapp/apps/admin/templates/index.html
@@ -0,0 +1 @@
+Hello from the Admin
diff --git a/websdk/flask/testsuite/test_apps/moduleapp/apps/frontend/__init__.py b/websdk/flask/testsuite/test_apps/moduleapp/apps/frontend/__init__.py
new file mode 100644
index 0000000..f83581e
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/moduleapp/apps/frontend/__init__.py
@@ -0,0 +1,9 @@
+from flask import Module, render_template
+
+
+frontend = Module(__name__)
+
+
+@frontend.route('/')
+def index():
+ return render_template('frontend/index.html')
diff --git a/websdk/flask/testsuite/test_apps/moduleapp/apps/frontend/templates/index.html b/websdk/flask/testsuite/test_apps/moduleapp/apps/frontend/templates/index.html
new file mode 100644
index 0000000..a062d71
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/moduleapp/apps/frontend/templates/index.html
@@ -0,0 +1 @@
+Hello from the Frontend
diff --git a/websdk/flask/testsuite/test_apps/subdomaintestmodule/__init__.py b/websdk/flask/testsuite/test_apps/subdomaintestmodule/__init__.py
new file mode 100644
index 0000000..3c5e358
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/subdomaintestmodule/__init__.py
@@ -0,0 +1,4 @@
+from flask import Module
+
+
+mod = Module(__name__, 'foo', subdomain='foo')
diff --git a/websdk/flask/testsuite/test_apps/subdomaintestmodule/static/hello.txt b/websdk/flask/testsuite/test_apps/subdomaintestmodule/static/hello.txt
new file mode 100644
index 0000000..12e23c1
--- /dev/null
+++ b/websdk/flask/testsuite/test_apps/subdomaintestmodule/static/hello.txt
@@ -0,0 +1 @@
+Hello Subdomain
diff --git a/websdk/flask/testsuite/testing.py b/websdk/flask/testsuite/testing.py
new file mode 100644
index 0000000..6574e77
--- /dev/null
+++ b/websdk/flask/testsuite/testing.py
@@ -0,0 +1,171 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.testing
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Test client and more.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import flask
+import unittest
+from flask.testsuite import FlaskTestCase
+
+
+class TestToolsTestCase(FlaskTestCase):
+
+ def test_environ_defaults_from_config(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ app.config['SERVER_NAME'] = 'example.com:1234'
+ app.config['APPLICATION_ROOT'] = '/foo'
+ @app.route('/')
+ def index():
+ return flask.request.url
+
+ ctx = app.test_request_context()
+ self.assert_equal(ctx.request.url, 'http://example.com:1234/foo/')
+ with app.test_client() as c:
+ rv = c.get('/')
+ self.assert_equal(rv.data, 'http://example.com:1234/foo/')
+
+ def test_environ_defaults(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ @app.route('/')
+ def index():
+ return flask.request.url
+
+ ctx = app.test_request_context()
+ self.assert_equal(ctx.request.url, 'http://localhost/')
+ with app.test_client() as c:
+ rv = c.get('/')
+ self.assert_equal(rv.data, 'http://localhost/')
+
+ def test_session_transactions(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ app.secret_key = 'testing'
+
+ @app.route('/')
+ def index():
+ return unicode(flask.session['foo'])
+
+ with app.test_client() as c:
+ with c.session_transaction() as sess:
+ self.assert_equal(len(sess), 0)
+ sess['foo'] = [42]
+ self.assert_equal(len(sess), 1)
+ rv = c.get('/')
+ self.assert_equal(rv.data, '[42]')
+ with c.session_transaction() as sess:
+ self.assert_equal(len(sess), 1)
+ self.assert_equal(sess['foo'], [42])
+
+ def test_session_transactions_no_null_sessions(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+
+ with app.test_client() as c:
+ try:
+ with c.session_transaction() as sess:
+ pass
+ except RuntimeError, e:
+ self.assert_('Session backend did not open a session' in str(e))
+ else:
+ self.fail('Expected runtime error')
+
+ def test_session_transactions_keep_context(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ app.secret_key = 'testing'
+
+ with app.test_client() as c:
+ rv = c.get('/')
+ req = flask.request._get_current_object()
+ with c.session_transaction():
+ self.assert_(req is flask.request._get_current_object())
+
+ def test_session_transaction_needs_cookies(self):
+ app = flask.Flask(__name__)
+ app.testing = True
+ c = app.test_client(use_cookies=False)
+ try:
+ with c.session_transaction() as s:
+ pass
+ except RuntimeError, e:
+ self.assert_('cookies' in str(e))
+ else:
+ self.fail('Expected runtime error')
+
+ def test_test_client_context_binding(self):
+ app = flask.Flask(__name__)
+ @app.route('/')
+ def index():
+ flask.g.value = 42
+ return 'Hello World!'
+
+ @app.route('/other')
+ def other():
+ 1/0
+
+ with app.test_client() as c:
+ resp = c.get('/')
+ self.assert_equal(flask.g.value, 42)
+ self.assert_equal(resp.data, 'Hello World!')
+ self.assert_equal(resp.status_code, 200)
+
+ resp = c.get('/other')
+ self.assert_(not hasattr(flask.g, 'value'))
+ self.assert_('Internal Server Error' in resp.data)
+ self.assert_equal(resp.status_code, 500)
+ flask.g.value = 23
+
+ try:
+ flask.g.value
+ except (AttributeError, RuntimeError):
+ pass
+ else:
+ raise AssertionError('some kind of exception expected')
+
+ def test_reuse_client(self):
+ app = flask.Flask(__name__)
+ c = app.test_client()
+
+ with c:
+ self.assert_equal(c.get('/').status_code, 404)
+
+ with c:
+ self.assert_equal(c.get('/').status_code, 404)
+
+ def test_test_client_calls_teardown_handlers(self):
+ app = flask.Flask(__name__)
+ called = []
+ @app.teardown_request
+ def remember(error):
+ called.append(error)
+
+ with app.test_client() as c:
+ self.assert_equal(called, [])
+ c.get('/')
+ self.assert_equal(called, [])
+ self.assert_equal(called, [None])
+
+ del called[:]
+ with app.test_client() as c:
+ self.assert_equal(called, [])
+ c.get('/')
+ self.assert_equal(called, [])
+ c.get('/')
+ self.assert_equal(called, [None])
+ self.assert_equal(called, [None, None])
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestToolsTestCase))
+ return suite
diff --git a/websdk/flask/testsuite/views.py b/websdk/flask/testsuite/views.py
new file mode 100644
index 0000000..c7cb0a8
--- /dev/null
+++ b/websdk/flask/testsuite/views.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.testsuite.views
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Pluggable views.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import flask
+import flask.views
+import unittest
+from flask.testsuite import FlaskTestCase
+from werkzeug.http import parse_set_header
+
+
+class ViewTestCase(FlaskTestCase):
+
+ def common_test(self, app):
+ c = app.test_client()
+
+ self.assert_equal(c.get('/').data, 'GET')
+ self.assert_equal(c.post('/').data, 'POST')
+ self.assert_equal(c.put('/').status_code, 405)
+ meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow'])
+ self.assert_equal(sorted(meths), ['GET', 'HEAD', 'OPTIONS', 'POST'])
+
+ def test_basic_view(self):
+ app = flask.Flask(__name__)
+
+ class Index(flask.views.View):
+ methods = ['GET', 'POST']
+ def dispatch_request(self):
+ return flask.request.method
+
+ app.add_url_rule('/', view_func=Index.as_view('index'))
+ self.common_test(app)
+
+ def test_method_based_view(self):
+ app = flask.Flask(__name__)
+
+ class Index(flask.views.MethodView):
+ def get(self):
+ return 'GET'
+ def post(self):
+ return 'POST'
+
+ app.add_url_rule('/', view_func=Index.as_view('index'))
+
+ self.common_test(app)
+
+ def test_view_patching(self):
+ app = flask.Flask(__name__)
+
+ class Index(flask.views.MethodView):
+ def get(self):
+ 1/0
+ def post(self):
+ 1/0
+
+ class Other(Index):
+ def get(self):
+ return 'GET'
+ def post(self):
+ return 'POST'
+
+ view = Index.as_view('index')
+ view.view_class = Other
+ app.add_url_rule('/', view_func=view)
+ self.common_test(app)
+
+ def test_view_inheritance(self):
+ app = flask.Flask(__name__)
+
+ class Index(flask.views.MethodView):
+ def get(self):
+ return 'GET'
+ def post(self):
+ return 'POST'
+
+ class BetterIndex(Index):
+ def delete(self):
+ return 'DELETE'
+
+ app.add_url_rule('/', view_func=BetterIndex.as_view('index'))
+ c = app.test_client()
+
+ meths = parse_set_header(c.open('/', method='OPTIONS').headers['Allow'])
+ self.assert_equal(sorted(meths), ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST'])
+
+ def test_view_decorators(self):
+ app = flask.Flask(__name__)
+
+ def add_x_parachute(f):
+ def new_function(*args, **kwargs):
+ resp = flask.make_response(f(*args, **kwargs))
+ resp.headers['X-Parachute'] = 'awesome'
+ return resp
+ return new_function
+
+ class Index(flask.views.View):
+ decorators = [add_x_parachute]
+ def dispatch_request(self):
+ return 'Awesome'
+
+ app.add_url_rule('/', view_func=Index.as_view('index'))
+ c = app.test_client()
+ rv = c.get('/')
+ self.assert_equal(rv.headers['X-Parachute'], 'awesome')
+ self.assert_equal(rv.data, 'Awesome')
+
+ def test_implicit_head(self):
+ app = flask.Flask(__name__)
+
+ class Index(flask.views.MethodView):
+ def get(self):
+ return flask.Response('Blub', headers={
+ 'X-Method': flask.request.method
+ })
+
+ app.add_url_rule('/', view_func=Index.as_view('index'))
+ c = app.test_client()
+ rv = c.get('/')
+ self.assert_equal(rv.data, 'Blub')
+ self.assert_equal(rv.headers['X-Method'], 'GET')
+ rv = c.head('/')
+ self.assert_equal(rv.data, '')
+ self.assert_equal(rv.headers['X-Method'], 'HEAD')
+
+ def test_explicit_head(self):
+ app = flask.Flask(__name__)
+
+ class Index(flask.views.MethodView):
+ def get(self):
+ return 'GET'
+ def head(self):
+ return flask.Response('', headers={'X-Method': 'HEAD'})
+
+ app.add_url_rule('/', view_func=Index.as_view('index'))
+ c = app.test_client()
+ rv = c.get('/')
+ self.assert_equal(rv.data, 'GET')
+ rv = c.head('/')
+ self.assert_equal(rv.data, '')
+ self.assert_equal(rv.headers['X-Method'], 'HEAD')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ViewTestCase))
+ return suite
diff --git a/websdk/flask/views.py b/websdk/flask/views.py
new file mode 100644
index 0000000..be718cc
--- /dev/null
+++ b/websdk/flask/views.py
@@ -0,0 +1,151 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.views
+ ~~~~~~~~~~~
+
+ This module provides class based views inspired by the ones in Django.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from .globals import request
+
+
+http_method_funcs = frozenset(['get', 'post', 'head', 'options',
+ 'delete', 'put', 'trace'])
+
+
+
+class View(object):
+ """Alternative way to use view functions. A subclass has to implement
+ :meth:`dispatch_request` which is called with the view arguments from
+ the URL routing system. If :attr:`methods` is provided the methods
+ do not have to be passed to the :meth:`~flask.Flask.add_url_rule`
+ method explicitly::
+
+ class MyView(View):
+ methods = ['GET']
+
+ def dispatch_request(self, name):
+ return 'Hello %s!' % name
+
+ app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))
+
+ 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
+ :meth:`as_view`) or you can use the :attr:`decorators` attribute::
+
+ class SecretView(View):
+ methods = ['GET']
+ decorators = [superuser_required]
+
+ def dispatch_request(self):
+ ...
+
+ The decorators stored in the decorators list are applied one after another
+ when the view function is created. Note that you can *not* use the class
+ based decorators since those would decorate the view class and not the
+ generated view function!
+ """
+
+ #: A for which methods this pluggable view can handle.
+ methods = None
+
+ #: 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's hooked
+ #: into the routing system.
+ #:
+ #: You can place one or more decorators in this list and whenever the
+ #: view function is created the result is automatically decorated.
+ #:
+ #: .. versionadded:: 0.8
+ decorators = []
+
+ def dispatch_request(self):
+ """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.
+ """
+ raise NotImplementedError()
+
+ @classmethod
+ def as_view(cls, name, *class_args, **class_kwargs):
+ """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 :class:`View`
+ on each request and call the :meth:`dispatch_request` method on it.
+
+ The arguments passed to :meth:`as_view` are forwarded to the
+ constructor of the class.
+ """
+ def view(*args, **kwargs):
+ self = view.view_class(*class_args, **class_kwargs)
+ return self.dispatch_request(*args, **kwargs)
+
+ if cls.decorators:
+ view.__name__ = name
+ view.__module__ = cls.__module__
+ for decorator in cls.decorators:
+ view = decorator(view)
+
+ # we attach the view class to the view function for two reasons:
+ # first of all it allows us to easily figure out what class based
+ # view this thing came from, secondly it's also used for instanciating
+ # the view class so you can actually replace it with something else
+ # for testing purposes and debugging.
+ view.view_class = cls
+ view.__name__ = name
+ view.__doc__ = cls.__doc__
+ view.__module__ = cls.__module__
+ view.methods = cls.methods
+ return view
+
+
+class MethodViewType(type):
+
+ def __new__(cls, name, bases, d):
+ rv = type.__new__(cls, name, bases, d)
+ if 'methods' not in d:
+ methods = set(rv.methods or [])
+ for key, value in d.iteritems():
+ if key in http_method_funcs:
+ methods.add(key.upper())
+ # if we have no method at all in there we don't want to
+ # add a method list. (This is for instance the case for
+ # the baseclass or another subclass of a base method view
+ # that does not introduce new methods).
+ if methods:
+ rv.methods = sorted(methods)
+ return rv
+
+
+class MethodView(View):
+ """Like a regular class based view but that dispatches requests to
+ particular methods. For instance if you implement a method called
+ :meth:`get` it means you will response to ``'GET'`` requests and
+ the :meth:`dispatch_request` implementation will automatically
+ forward your request to that. Also :attr:`options` is set for you
+ automatically::
+
+ class CounterAPI(MethodView):
+
+ def get(self):
+ return session.get('counter', 0)
+
+ def post(self):
+ session['counter'] = session.get('counter', 0) + 1
+ return 'OK'
+
+ app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
+ """
+ __metaclass__ = MethodViewType
+
+ def dispatch_request(self, *args, **kwargs):
+ meth = getattr(self, request.method.lower(), None)
+ # if the request method is HEAD and we don't have a handler for it
+ # retry with GET
+ if meth is None and request.method == 'HEAD':
+ meth = getattr(self, 'get', None)
+ assert meth is not None, 'Not implemented method %r' % request.method
+ return meth(*args, **kwargs)
diff --git a/websdk/flask/wrappers.py b/websdk/flask/wrappers.py
new file mode 100644
index 0000000..f6ec278
--- /dev/null
+++ b/websdk/flask/wrappers.py
@@ -0,0 +1,138 @@
+# -*- coding: utf-8 -*-
+"""
+ flask.wrappers
+ ~~~~~~~~~~~~~~
+
+ Implements the WSGI wrappers (request and response).
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase
+from werkzeug.exceptions import BadRequest
+from werkzeug.utils import cached_property
+
+from .debughelpers import attach_enctype_error_multidict
+from .helpers import json, _assert_have_json
+from .globals import _request_ctx_stack
+
+
+class Request(RequestBase):
+ """The request object used by default in Flask. Remembers the
+ matched endpoint and view arguments.
+
+ It is what ends up as :class:`~flask.request`. If you want to replace
+ the request object used you can subclass this and set
+ :attr:`~flask.Flask.request_class` to your subclass.
+
+ The request object is a :class:`~werkzeug.wrappers.Request` subclass and
+ provides all of the attributes Werkzeug defines plus a few Flask
+ specific ones.
+ """
+
+ #: the internal URL rule that matched the request. This can be
+ #: useful to inspect which methods are allowed for the URL from
+ #: a before/after handler (``request.url_rule.methods``) etc.
+ #:
+ #: .. versionadded:: 0.6
+ url_rule = None
+
+ #: a dict of view arguments that matched the request. If an exception
+ #: happened when matching, this will be `None`.
+ view_args = None
+
+ #: if matching the URL failed, this is the exception that will be
+ #: raised / was raised as part of the request handling. This is
+ #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
+ #: something similar.
+ routing_exception = None
+
+ # switched by the request context until 1.0 to opt in deprecated
+ # module functionality
+ _is_old_module = False
+
+ @property
+ def max_content_length(self):
+ """Read-only view of the `MAX_CONTENT_LENGTH` config key."""
+ ctx = _request_ctx_stack.top
+ if ctx is not None:
+ return ctx.app.config['MAX_CONTENT_LENGTH']
+
+ @property
+ def endpoint(self):
+ """The endpoint that matched the request. This in combination with
+ :attr:`view_args` can be used to reconstruct the same or a
+ modified URL. If an exception happened when matching, this will
+ be `None`.
+ """
+ if self.url_rule is not None:
+ return self.url_rule.endpoint
+
+ @property
+ def module(self):
+ """The name of the current module if the request was dispatched
+ to an actual module. This is deprecated functionality, use blueprints
+ instead.
+ """
+ from warnings import warn
+ warn(DeprecationWarning('modules were deprecated in favor of '
+ 'blueprints. Use request.blueprint '
+ 'instead.'), stacklevel=2)
+ if self._is_old_module:
+ return self.blueprint
+
+ @property
+ def blueprint(self):
+ """The name of the current blueprint"""
+ if self.url_rule and '.' in self.url_rule.endpoint:
+ return self.url_rule.endpoint.rsplit('.', 1)[0]
+
+ @cached_property
+ def json(self):
+ """If the mimetype is `application/json` this will contain the
+ parsed JSON data. Otherwise this will be `None`.
+
+ This requires Python 2.6 or an installed version of simplejson.
+ """
+ if __debug__:
+ _assert_have_json()
+ if self.mimetype == 'application/json':
+ request_charset = self.mimetype_params.get('charset')
+ try:
+ if request_charset is not None:
+ return json.loads(self.data, encoding=request_charset)
+ return json.loads(self.data)
+ except ValueError, e:
+ return self.on_json_loading_failed(e)
+
+ def on_json_loading_failed(self, e):
+ """Called if decoding of the JSON data failed. The return value of
+ this method is used by :attr:`json` when an error ocurred. The
+ default implementation raises a :class:`~werkzeug.exceptions.BadRequest`.
+
+ .. versionadded:: 0.8
+ """
+ raise BadRequest()
+
+ def _load_form_data(self):
+ RequestBase._load_form_data(self)
+
+ # in debug mode we're replacing the files multidict with an ad-hoc
+ # subclass that raises a different error for key errors.
+ ctx = _request_ctx_stack.top
+ if ctx is not None and ctx.app.debug and \
+ self.mimetype != 'multipart/form-data' and not self.files:
+ attach_enctype_error_multidict(self)
+
+
+class Response(ResponseBase):
+ """The response object that is used by default in Flask. Works like the
+ response object from Werkzeug but is set to have an HTML mimetype by
+ default. Quite often you don't have to create this object yourself because
+ :meth:`~flask.Flask.make_response` will take care of that for you.
+
+ If you want to replace the response object used you can subclass this and
+ set :attr:`~flask.Flask.response_class` to your subclass.
+ """
+ default_mimetype = 'text/html'
diff --git a/websdk/flaskext/__init__.py b/websdk/flaskext/__init__.py
new file mode 100644
index 0000000..de40ea7
--- /dev/null
+++ b/websdk/flaskext/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/websdk/flaskext/genshi.py b/websdk/flaskext/genshi.py
new file mode 100644
index 0000000..a2bbbe5
--- /dev/null
+++ b/websdk/flaskext/genshi.py
@@ -0,0 +1,294 @@
+# -*- coding: utf-8 -*-
+"""
+ flaskext.genshi
+ ~~~~~~~~~~~~~~~
+
+ An extension to Flask for easy Genshi templating.
+
+ :copyright: (c) 2010 by Dag Odenhall <dag.odenhall@gmail.com>.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import absolute_import
+
+from collections import defaultdict
+import os.path
+from warnings import warn
+from inspect import getargspec
+
+from genshi.template import (NewTextTemplate, MarkupTemplate,
+ loader, TemplateLoader)
+from werkzeug import cached_property
+from flask import current_app
+
+try:
+ from flask import signals_available
+except ImportError:
+ signals_available = False
+else:
+ from flask.signals import Namespace
+ signals = Namespace()
+ template_generated = signals.signal('template-generated')
+
+
+class Genshi(object):
+ """Initialize extension.
+
+ ::
+
+ app = Flask(__name__)
+ genshi = Genshi(app)
+
+ .. versionchanged:: 0.4
+ You can now initialize your application later with :meth:`init_app`.
+
+ .. deprecated:: 0.4
+ ``app.genshi_instance`` in favor of ``app.extensions['genshi']``.
+
+ """
+
+ def __init__(self, app=None):
+
+ if app is not None:
+ self.init_app(app)
+
+ #: A callable for Genshi's callback interface, called when a template
+ #: is loaded, with the template as the only argument.
+ #:
+ #: :meth:`template_parsed` is a decorator for setting this.
+ #:
+ #: .. versionadded:: 0.5
+ self.callback = None
+
+ #: What method is used for an extension.
+ self.extensions = {
+ 'html': 'html',
+ 'xml': 'xml',
+ 'txt': 'text',
+ 'js': 'js',
+ 'css': 'css',
+ 'svg': 'svg'
+ }
+
+ #: Render methods.
+ #:
+ #: .. versionchanged:: 0.3 Support for Javascript and CSS.
+ #: .. versionchanged:: 0.4 Support for SVG.
+ self.methods = {
+ 'html': {
+ 'serializer': 'html',
+ 'doctype': 'html',
+ },
+ 'html5': {
+ 'serializer': 'html',
+ 'doctype': 'html5',
+ },
+ 'xhtml': {
+ 'serializer': 'xhtml',
+ 'doctype': 'xhtml',
+ 'mimetype': 'application/xhtml+xml'
+ },
+ 'xml': {
+ 'serializer': 'xml',
+ 'mimetype': 'application/xml'
+ },
+ 'text': {
+ 'serializer': 'text',
+ 'mimetype': 'text/plain',
+ 'class': NewTextTemplate
+ },
+ 'js': {
+ 'serializer': 'text',
+ 'mimetype': 'application/javascript',
+ 'class': NewTextTemplate
+ },
+ 'css': {
+ 'serializer': 'text',
+ 'mimetype': 'text/css',
+ 'class': NewTextTemplate
+ },
+ 'svg': {
+ 'serializer': 'xml',
+ 'doctype': 'svg',
+ 'mimetype': 'image/svg+xml'
+ }
+ }
+
+ #: Filter functions to be applied to templates.
+ #:
+ #: .. versionadded:: 0.3
+ self.filters = defaultdict(list)
+
+ def init_app(self, app):
+ """Initialize a :class:`~flask.Flask` application
+ for use with this extension. Useful for the factory pattern but
+ not needed if you passed your application to the :class:`Genshi`
+ constructor.
+
+ ::
+
+ genshi = Genshi()
+
+ app = Flask(__name__)
+ genshi.init_app(app)
+
+ .. versionadded:: 0.4
+
+ """
+ if not hasattr(app, 'extensions'):
+ app.extensions = {}
+
+ app.extensions['genshi'] = self
+ app.genshi_instance = self
+ self.app = app
+
+ def template_parsed(self, callback):
+ """Set up a calback to be called with a template when it is first
+ loaded and parsed. This is the correct way to set up the
+ :class:`~genshi.filters.Translator` filter.
+
+ .. versionadded:: 0.5
+
+ """
+ self.callback = callback
+ return callback
+
+ @cached_property
+ def template_loader(self):
+ """A :class:`genshi.template.TemplateLoader` that loads templates
+ from the same places as Flask.
+
+ """
+ path = loader.directory(os.path.join(self.app.root_path, 'templates'))
+ module_paths = {}
+ modules = getattr(self.app, 'modules', {})
+ for name, module in modules.iteritems():
+ module_path = os.path.join(module.root_path, 'templates')
+ if os.path.isdir(module_path):
+ module_paths[name] = loader.directory(module_path)
+ return TemplateLoader([path, loader.prefixed(**module_paths)],
+ auto_reload=self.app.debug,
+ callback=self.callback)
+
+ def filter(self, *methods):
+ """Decorator that adds a function to apply filters
+ to templates by rendering method.
+
+ .. versionadded:: 0.3
+
+ .. versionchanged:: 0.5
+ Filters can now optionally take a second argument for the context.
+
+ """
+ def decorator(function):
+ for method in methods:
+ self.filters[method].append(function)
+ return function
+ return decorator
+
+ def _method_for(self, template, method=None):
+ """Selects a method from :attr:`Genshi.methods`
+ based on the file extension of ``template``
+ and :attr:`Genshi.extensions`, or based on ``method``.
+
+ """
+ if method is None:
+ ext = os.path.splitext(template)[1][1:]
+ return self.extensions[ext]
+ return method
+
+
+def select_method(template, method=None):
+ """Same as :meth:`Genshi._method_for`.
+
+ .. deprecated:: 0.4
+
+ """
+ warn('select_method to be dropped in future releases',
+ DeprecationWarning, stacklevel=2)
+ return current_app.extensions['genshi']._method_for(template, method)
+
+
+def generate_template(template=None, context=None,
+ method=None, string=None, filter=None):
+ """Creates a Genshi template stream that you can
+ run filters and transformations on.
+
+ """
+ genshi = current_app.extensions['genshi']
+ method = genshi._method_for(template, method)
+ class_ = genshi.methods[method].get('class', MarkupTemplate)
+
+ context = context or {}
+ for key, value in current_app.jinja_env.globals.iteritems():
+ context.setdefault(key, value)
+ context.setdefault('filters', current_app.jinja_env.filters)
+ context.setdefault('tests', current_app.jinja_env.tests)
+ for key, value in current_app.jinja_env.filters.iteritems():
+ context.setdefault(key, value)
+ for key, value in current_app.jinja_env.tests.iteritems():
+ context.setdefault('is%s' % key, value)
+ current_app.update_template_context(context)
+
+ if template is not None:
+ template = genshi.template_loader.load(template, cls=class_)
+ elif string is not None:
+ template = class_(string)
+ else:
+ raise RuntimeError('Need a template or string')
+
+ stream = template.generate(**context)
+
+ if signals_available:
+ template_generated.send(current_app._get_current_object(),
+ template=template, context=context)
+
+ for func in genshi.filters[method]:
+ if len(getargspec(func)[0]) == 2: # Filter takes context?
+ stream = func(stream, context)
+ else:
+ stream = func(stream)
+
+ if filter:
+ if len(getargspec(filter)[0]) == 2: # Filter takes context?
+ stream = filter(stream, context)
+ else:
+ stream = filter(stream)
+
+ return stream
+
+
+def render_template(template=None, context=None,
+ method=None, string=None, filter=None):
+ """Renders a template to a string."""
+ genshi = current_app.extensions['genshi']
+ method = genshi._method_for(template, method)
+ template = generate_template(template, context, method, string, filter)
+ render_args = dict(method=genshi.methods[method]['serializer'])
+ if 'doctype' in genshi.methods[method]:
+ render_args['doctype'] = genshi.methods[method]['doctype']
+ return template.render(**render_args)
+
+
+def render_response(template=None, context=None,
+ method=None, string=None, filter=None):
+ """Renders a template and wraps it in a :attr:`~flask.Flask.response_class`
+ with mimetype set according to the rendering method.
+
+ """
+ genshi = current_app.extensions['genshi']
+ method = genshi._method_for(template, method)
+ mimetype = genshi.methods[method].get('mimetype', 'text/html')
+ template = render_template(template, context, method, string, filter)
+ return current_app.response_class(template, mimetype=mimetype)
+
+
+def render(template, **context):
+ """Render a template to a response object, passing the context as
+ keyword arguments. Shorthand for
+ ``render_response(template, dict(**context))``.
+
+ .. versionadded:: 0.6
+
+ """
+ return render_response(template, context)
diff --git a/websdk/genshi/__init__.py b/websdk/genshi/__init__.py
new file mode 100644
index 0000000..02f4347
--- /dev/null
+++ b/websdk/genshi/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""This package provides various means for generating and processing web markup
+(XML or HTML).
+
+The design is centered around the concept of streams of markup events (similar
+in concept to SAX parsing events) which can be processed in a uniform manner
+independently of where or how they are produced.
+"""
+
+__docformat__ = 'restructuredtext en'
+__version__ = '0.6'
+
+from genshi.core import *
+from genshi.input import ParseError, XML, HTML
diff --git a/websdk/genshi/builder.py b/websdk/genshi/builder.py
new file mode 100644
index 0000000..724e364
--- /dev/null
+++ b/websdk/genshi/builder.py
@@ -0,0 +1,359 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Support for programmatically generating markup streams from Python code using
+a very simple syntax. The main entry point to this module is the `tag` object
+(which is actually an instance of the ``ElementFactory`` class). You should
+rarely (if ever) need to directly import and use any of the other classes in
+this module.
+
+Elements can be created using the `tag` object using attribute access. For
+example:
+
+>>> doc = tag.p('Some text and ', tag.a('a link', href='http://example.org/'), '.')
+>>> doc
+<Element "p">
+
+This produces an `Element` instance which can be further modified to add child
+nodes and attributes. This is done by "calling" the element: positional
+arguments are added as child nodes (alternatively, the `Element.append` method
+can be used for that purpose), whereas keywords arguments are added as
+attributes:
+
+>>> doc(tag.br)
+<Element "p">
+>>> print(doc)
+<p>Some text and <a href="http://example.org/">a link</a>.<br/></p>
+
+If an attribute name collides with a Python keyword, simply append an underscore
+to the name:
+
+>>> doc(class_='intro')
+<Element "p">
+>>> print(doc)
+<p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p>
+
+As shown above, an `Element` can easily be directly rendered to XML text by
+printing it or using the Python ``str()`` function. This is basically a
+shortcut for converting the `Element` to a stream and serializing that
+stream:
+
+>>> stream = doc.generate()
+>>> stream #doctest: +ELLIPSIS
+<genshi.core.Stream object at ...>
+>>> print(stream)
+<p class="intro">Some text and <a href="http://example.org/">a link</a>.<br/></p>
+
+
+The `tag` object also allows creating "fragments", which are basically lists
+of nodes (elements or text) that don't have a parent element. This can be useful
+for creating snippets of markup that are attached to a parent element later (for
+example in a template). Fragments are created by calling the `tag` object, which
+returns an object of type `Fragment`:
+
+>>> fragment = tag('Hello, ', tag.em('world'), '!')
+>>> fragment
+<Fragment>
+>>> print(fragment)
+Hello, <em>world</em>!
+"""
+
+from genshi.core import Attrs, Markup, Namespace, QName, Stream, \
+ START, END, TEXT
+
+__all__ = ['Fragment', 'Element', 'ElementFactory', 'tag']
+__docformat__ = 'restructuredtext en'
+
+
+class Fragment(object):
+ """Represents a markup fragment, which is basically just a list of element
+ or text nodes.
+ """
+ __slots__ = ['children']
+
+ def __init__(self):
+ """Create a new fragment."""
+ self.children = []
+
+ def __add__(self, other):
+ return Fragment()(self, other)
+
+ def __call__(self, *args):
+ """Append any positional arguments as child nodes.
+
+ :see: `append`
+ """
+ for arg in args:
+ self.append(arg)
+ return self
+
+ def __iter__(self):
+ return self._generate()
+
+ def __repr__(self):
+ return '<%s>' % type(self).__name__
+
+ def __str__(self):
+ return str(self.generate())
+
+ def __unicode__(self):
+ return unicode(self.generate())
+
+ def __html__(self):
+ return Markup(self.generate())
+
+ def append(self, node):
+ """Append an element or string as child node.
+
+ :param node: the node to append; can be an `Element`, `Fragment`, or a
+ `Stream`, or a Python string or number
+ """
+ if isinstance(node, (Stream, Element, basestring, int, float, long)):
+ # For objects of a known/primitive type, we avoid the check for
+ # whether it is iterable for better performance
+ self.children.append(node)
+ elif isinstance(node, Fragment):
+ self.children.extend(node.children)
+ elif node is not None:
+ try:
+ for child in node:
+ self.append(child)
+ except TypeError:
+ self.children.append(node)
+
+ def _generate(self):
+ for child in self.children:
+ if isinstance(child, Fragment):
+ for event in child._generate():
+ yield event
+ elif isinstance(child, Stream):
+ for event in child:
+ yield event
+ else:
+ if not isinstance(child, basestring):
+ child = unicode(child)
+ yield TEXT, child, (None, -1, -1)
+
+ def generate(self):
+ """Return a markup event stream for the fragment.
+
+ :rtype: `Stream`
+ """
+ return Stream(self._generate())
+
+
+def _kwargs_to_attrs(kwargs):
+ attrs = []
+ names = set()
+ for name, value in kwargs.items():
+ name = name.rstrip('_').replace('_', '-')
+ if value is not None and name not in names:
+ attrs.append((QName(name), unicode(value)))
+ names.add(name)
+ return Attrs(attrs)
+
+
+class Element(Fragment):
+ """Simple XML output generator based on the builder pattern.
+
+ Construct XML elements by passing the tag name to the constructor:
+
+ >>> print(Element('strong'))
+ <strong/>
+
+ Attributes can be specified using keyword arguments. The values of the
+ arguments will be converted to strings and any special XML characters
+ escaped:
+
+ >>> print(Element('textarea', rows=10, cols=60))
+ <textarea rows="10" cols="60"/>
+ >>> print(Element('span', title='1 < 2'))
+ <span title="1 &lt; 2"/>
+ >>> print(Element('span', title='"baz"'))
+ <span title="&#34;baz&#34;"/>
+
+ The " character is escaped using a numerical entity.
+ The order in which attributes are rendered is undefined.
+
+ If an attribute value evaluates to `None`, that attribute is not included
+ in the output:
+
+ >>> print(Element('a', name=None))
+ <a/>
+
+ Attribute names that conflict with Python keywords can be specified by
+ appending an underscore:
+
+ >>> print(Element('div', class_='warning'))
+ <div class="warning"/>
+
+ Nested elements can be added to an element using item access notation.
+ The call notation can also be used for this and for adding attributes
+ using keyword arguments, as one would do in the constructor.
+
+ >>> print(Element('ul')(Element('li'), Element('li')))
+ <ul><li/><li/></ul>
+ >>> print(Element('a')('Label'))
+ <a>Label</a>
+ >>> print(Element('a')('Label', href="target"))
+ <a href="target">Label</a>
+
+ Text nodes can be nested in an element by adding strings instead of
+ elements. Any special characters in the strings are escaped automatically:
+
+ >>> print(Element('em')('Hello world'))
+ <em>Hello world</em>
+ >>> print(Element('em')(42))
+ <em>42</em>
+ >>> print(Element('em')('1 < 2'))
+ <em>1 &lt; 2</em>
+
+ This technique also allows mixed content:
+
+ >>> print(Element('p')('Hello ', Element('b')('world')))
+ <p>Hello <b>world</b></p>
+
+ Quotes are not escaped inside text nodes:
+ >>> print(Element('p')('"Hello"'))
+ <p>"Hello"</p>
+
+ Elements can also be combined with other elements or strings using the
+ addition operator, which results in a `Fragment` object that contains the
+ operands:
+
+ >>> print(Element('br') + 'some text' + Element('br'))
+ <br/>some text<br/>
+
+ Elements with a namespace can be generated using the `Namespace` and/or
+ `QName` classes:
+
+ >>> from genshi.core import Namespace
+ >>> xhtml = Namespace('http://www.w3.org/1999/xhtml')
+ >>> print(Element(xhtml.html, lang='en'))
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en"/>
+ """
+ __slots__ = ['tag', 'attrib']
+
+ def __init__(self, tag_, **attrib):
+ Fragment.__init__(self)
+ self.tag = QName(tag_)
+ self.attrib = _kwargs_to_attrs(attrib)
+
+ def __call__(self, *args, **kwargs):
+ """Append any positional arguments as child nodes, and keyword arguments
+ as attributes.
+
+ :return: the element itself so that calls can be chained
+ :rtype: `Element`
+ :see: `Fragment.append`
+ """
+ self.attrib |= _kwargs_to_attrs(kwargs)
+ Fragment.__call__(self, *args)
+ return self
+
+ def __repr__(self):
+ return '<%s "%s">' % (type(self).__name__, self.tag)
+
+ def _generate(self):
+ yield START, (self.tag, self.attrib), (None, -1, -1)
+ for kind, data, pos in Fragment._generate(self):
+ yield kind, data, pos
+ yield END, self.tag, (None, -1, -1)
+
+ def generate(self):
+ """Return a markup event stream for the fragment.
+
+ :rtype: `Stream`
+ """
+ return Stream(self._generate())
+
+
+class ElementFactory(object):
+ """Factory for `Element` objects.
+
+ A new element is created simply by accessing a correspondingly named
+ attribute of the factory object:
+
+ >>> factory = ElementFactory()
+ >>> print(factory.foo)
+ <foo/>
+ >>> print(factory.foo(id=2))
+ <foo id="2"/>
+
+ Markup fragments (lists of nodes without a parent element) can be created
+ by calling the factory:
+
+ >>> print(factory('Hello, ', factory.em('world'), '!'))
+ Hello, <em>world</em>!
+
+ A factory can also be bound to a specific namespace:
+
+ >>> factory = ElementFactory('http://www.w3.org/1999/xhtml')
+ >>> print(factory.html(lang="en"))
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en"/>
+
+ The namespace for a specific element can be altered on an existing factory
+ by specifying the new namespace using item access:
+
+ >>> factory = ElementFactory()
+ >>> print(factory.html(factory['http://www.w3.org/2000/svg'].g(id=3)))
+ <html><g xmlns="http://www.w3.org/2000/svg" id="3"/></html>
+
+ Usually, the `ElementFactory` class is not be used directly. Rather, the
+ `tag` instance should be used to create elements.
+ """
+
+ def __init__(self, namespace=None):
+ """Create the factory, optionally bound to the given namespace.
+
+ :param namespace: the namespace URI for any created elements, or `None`
+ for no namespace
+ """
+ if namespace and not isinstance(namespace, Namespace):
+ namespace = Namespace(namespace)
+ self.namespace = namespace
+
+ def __call__(self, *args):
+ """Create a fragment that has the given positional arguments as child
+ nodes.
+
+ :return: the created `Fragment`
+ :rtype: `Fragment`
+ """
+ return Fragment()(*args)
+
+ def __getitem__(self, namespace):
+ """Return a new factory that is bound to the specified namespace.
+
+ :param namespace: the namespace URI or `Namespace` object
+ :return: an `ElementFactory` that produces elements bound to the given
+ namespace
+ :rtype: `ElementFactory`
+ """
+ return ElementFactory(namespace)
+
+ def __getattr__(self, name):
+ """Create an `Element` with the given name.
+
+ :param name: the tag name of the element to create
+ :return: an `Element` with the specified name
+ :rtype: `Element`
+ """
+ return Element(self.namespace and self.namespace[name] or name)
+
+
+tag = ElementFactory()
+"""Global `ElementFactory` bound to the default namespace.
+
+:type: `ElementFactory`
+"""
diff --git a/websdk/genshi/core.py b/websdk/genshi/core.py
new file mode 100644
index 0000000..f7cddff
--- /dev/null
+++ b/websdk/genshi/core.py
@@ -0,0 +1,727 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Core classes for markup processing."""
+
+try:
+ reduce # builtin in Python < 3
+except NameError:
+ from functools import reduce
+from itertools import chain
+import operator
+
+from genshi.util import plaintext, stripentities, striptags, stringrepr
+
+__all__ = ['Stream', 'Markup', 'escape', 'unescape', 'Attrs', 'Namespace',
+ 'QName']
+__docformat__ = 'restructuredtext en'
+
+
+class StreamEventKind(str):
+ """A kind of event on a markup stream."""
+ __slots__ = []
+ _instances = {}
+
+ def __new__(cls, val):
+ return cls._instances.setdefault(val, str.__new__(cls, val))
+
+
+class Stream(object):
+ """Represents a stream of markup events.
+
+ This class is basically an iterator over the events.
+
+ Stream events are tuples of the form::
+
+ (kind, data, position)
+
+ where ``kind`` is the event kind (such as `START`, `END`, `TEXT`, etc),
+ ``data`` depends on the kind of event, and ``position`` is a
+ ``(filename, line, offset)`` tuple that contains the location of the
+ original element or text in the input. If the original location is unknown,
+ ``position`` is ``(None, -1, -1)``.
+
+ Also provided are ways to serialize the stream to text. The `serialize()`
+ method will return an iterator over generated strings, while `render()`
+ returns the complete generated text at once. Both accept various parameters
+ that impact the way the stream is serialized.
+ """
+ __slots__ = ['events', 'serializer']
+
+ START = StreamEventKind('START') #: a start tag
+ END = StreamEventKind('END') #: an end tag
+ TEXT = StreamEventKind('TEXT') #: literal text
+ XML_DECL = StreamEventKind('XML_DECL') #: XML declaration
+ DOCTYPE = StreamEventKind('DOCTYPE') #: doctype declaration
+ START_NS = StreamEventKind('START_NS') #: start namespace mapping
+ END_NS = StreamEventKind('END_NS') #: end namespace mapping
+ START_CDATA = StreamEventKind('START_CDATA') #: start CDATA section
+ END_CDATA = StreamEventKind('END_CDATA') #: end CDATA section
+ PI = StreamEventKind('PI') #: processing instruction
+ COMMENT = StreamEventKind('COMMENT') #: comment
+
+ def __init__(self, events, serializer=None):
+ """Initialize the stream with a sequence of markup events.
+
+ :param events: a sequence or iterable providing the events
+ :param serializer: the default serialization method to use for this
+ stream
+
+ :note: Changed in 0.5: added the `serializer` argument
+ """
+ self.events = events #: The underlying iterable producing the events
+ self.serializer = serializer #: The default serializion method
+
+ def __iter__(self):
+ return iter(self.events)
+
+ def __or__(self, function):
+ """Override the "bitwise or" operator to apply filters or serializers
+ to the stream, providing a syntax similar to pipes on Unix shells.
+
+ Assume the following stream produced by the `HTML` function:
+
+ >>> from genshi.input import HTML
+ >>> html = HTML('''<p onclick="alert('Whoa')">Hello, world!</p>''')
+ >>> print(html)
+ <p onclick="alert('Whoa')">Hello, world!</p>
+
+ A filter such as the HTML sanitizer can be applied to that stream using
+ the pipe notation as follows:
+
+ >>> from genshi.filters import HTMLSanitizer
+ >>> sanitizer = HTMLSanitizer()
+ >>> print(html | sanitizer)
+ <p>Hello, world!</p>
+
+ Filters can be any function that accepts and produces a stream (where
+ a stream is anything that iterates over events):
+
+ >>> def uppercase(stream):
+ ... for kind, data, pos in stream:
+ ... if kind is TEXT:
+ ... data = data.upper()
+ ... yield kind, data, pos
+ >>> print(html | sanitizer | uppercase)
+ <p>HELLO, WORLD!</p>
+
+ Serializers can also be used with this notation:
+
+ >>> from genshi.output import TextSerializer
+ >>> output = TextSerializer()
+ >>> print(html | sanitizer | uppercase | output)
+ HELLO, WORLD!
+
+ Commonly, serializers should be used at the end of the "pipeline";
+ using them somewhere in the middle may produce unexpected results.
+
+ :param function: the callable object that should be applied as a filter
+ :return: the filtered stream
+ :rtype: `Stream`
+ """
+ return Stream(_ensure(function(self)), serializer=self.serializer)
+
+ def filter(self, *filters):
+ """Apply filters to the stream.
+
+ This method returns a new stream with the given filters applied. The
+ filters must be callables that accept the stream object as parameter,
+ and return the filtered stream.
+
+ The call::
+
+ stream.filter(filter1, filter2)
+
+ is equivalent to::
+
+ stream | filter1 | filter2
+
+ :param filters: one or more callable objects that should be applied as
+ filters
+ :return: the filtered stream
+ :rtype: `Stream`
+ """
+ return reduce(operator.or_, (self,) + filters)
+
+ def render(self, method=None, encoding='utf-8', out=None, **kwargs):
+ """Return a string representation of the stream.
+
+ Any additional keyword arguments are passed to the serializer, and thus
+ depend on the `method` parameter value.
+
+ :param method: determines how the stream is serialized; can be either
+ "xml", "xhtml", "html", "text", or a custom serializer
+ class; if `None`, the default serialization method of
+ the stream is used
+ :param encoding: how the output string should be encoded; if set to
+ `None`, this method returns a `unicode` object
+ :param out: a file-like object that the output should be written to
+ instead of being returned as one big string; note that if
+ this is a file or socket (or similar), the `encoding` must
+ not be `None` (that is, the output must be encoded)
+ :return: a `str` or `unicode` object (depending on the `encoding`
+ parameter), or `None` if the `out` parameter is provided
+ :rtype: `basestring`
+
+ :see: XMLSerializer, XHTMLSerializer, HTMLSerializer, TextSerializer
+ :note: Changed in 0.5: added the `out` parameter
+ """
+ from genshi.output import encode
+ if method is None:
+ method = self.serializer or 'xml'
+ generator = self.serialize(method=method, **kwargs)
+ return encode(generator, method=method, encoding=encoding, out=out)
+
+ def select(self, path, namespaces=None, variables=None):
+ """Return a new stream that contains the events matching the given
+ XPath expression.
+
+ >>> from genshi import HTML
+ >>> stream = HTML('<doc><elem>foo</elem><elem>bar</elem></doc>')
+ >>> print(stream.select('elem'))
+ <elem>foo</elem><elem>bar</elem>
+ >>> print(stream.select('elem/text()'))
+ foobar
+
+ Note that the outermost element of the stream becomes the *context
+ node* for the XPath test. That means that the expression "doc" would
+ not match anything in the example above, because it only tests against
+ child elements of the outermost element:
+
+ >>> print(stream.select('doc'))
+ <BLANKLINE>
+
+ You can use the "." expression to match the context node itself
+ (although that usually makes little sense):
+
+ >>> print(stream.select('.'))
+ <doc><elem>foo</elem><elem>bar</elem></doc>
+
+ :param path: a string containing the XPath expression
+ :param namespaces: mapping of namespace prefixes used in the path
+ :param variables: mapping of variable names to values
+ :return: the selected substream
+ :rtype: `Stream`
+ :raises PathSyntaxError: if the given path expression is invalid or not
+ supported
+ """
+ from genshi.path import Path
+ return Path(path).select(self, namespaces, variables)
+
+ def serialize(self, method='xml', **kwargs):
+ """Generate strings corresponding to a specific serialization of the
+ stream.
+
+ Unlike the `render()` method, this method is a generator that returns
+ the serialized output incrementally, as opposed to returning a single
+ string.
+
+ Any additional keyword arguments are passed to the serializer, and thus
+ depend on the `method` parameter value.
+
+ :param method: determines how the stream is serialized; can be either
+ "xml", "xhtml", "html", "text", or a custom serializer
+ class; if `None`, the default serialization method of
+ the stream is used
+ :return: an iterator over the serialization results (`Markup` or
+ `unicode` objects, depending on the serialization method)
+ :rtype: ``iterator``
+ :see: XMLSerializer, XHTMLSerializer, HTMLSerializer, TextSerializer
+ """
+ from genshi.output import get_serializer
+ if method is None:
+ method = self.serializer or 'xml'
+ return get_serializer(method, **kwargs)(_ensure(self))
+
+ def __str__(self):
+ return self.render()
+
+ def __unicode__(self):
+ return self.render(encoding=None)
+
+ def __html__(self):
+ return self
+
+
+START = Stream.START
+END = Stream.END
+TEXT = Stream.TEXT
+XML_DECL = Stream.XML_DECL
+DOCTYPE = Stream.DOCTYPE
+START_NS = Stream.START_NS
+END_NS = Stream.END_NS
+START_CDATA = Stream.START_CDATA
+END_CDATA = Stream.END_CDATA
+PI = Stream.PI
+COMMENT = Stream.COMMENT
+
+
+def _ensure(stream):
+ """Ensure that every item on the stream is actually a markup event."""
+ stream = iter(stream)
+ event = stream.next()
+
+ # Check whether the iterable is a real markup event stream by examining the
+ # first item it yields; if it's not we'll need to do some conversion
+ if type(event) is not tuple or len(event) != 3:
+ for event in chain([event], stream):
+ if hasattr(event, 'totuple'):
+ event = event.totuple()
+ else:
+ event = TEXT, unicode(event), (None, -1, -1)
+ yield event
+ return
+
+ # This looks like a markup event stream, so we'll just pass it through
+ # unchanged
+ yield event
+ for event in stream:
+ yield event
+
+
+class Attrs(tuple):
+ """Immutable sequence type that stores the attributes of an element.
+
+ Ordering of the attributes is preserved, while access by name is also
+ supported.
+
+ >>> attrs = Attrs([('href', '#'), ('title', 'Foo')])
+ >>> attrs
+ Attrs([('href', '#'), ('title', 'Foo')])
+
+ >>> 'href' in attrs
+ True
+ >>> 'tabindex' in attrs
+ False
+ >>> attrs.get('title')
+ 'Foo'
+
+ Instances may not be manipulated directly. Instead, the operators ``|`` and
+ ``-`` can be used to produce new instances that have specific attributes
+ added, replaced or removed.
+
+ To remove an attribute, use the ``-`` operator. The right hand side can be
+ either a string or a set/sequence of strings, identifying the name(s) of
+ the attribute(s) to remove:
+
+ >>> attrs - 'title'
+ Attrs([('href', '#')])
+ >>> attrs - ('title', 'href')
+ Attrs()
+
+ The original instance is not modified, but the operator can of course be
+ used with an assignment:
+
+ >>> attrs
+ Attrs([('href', '#'), ('title', 'Foo')])
+ >>> attrs -= 'title'
+ >>> attrs
+ Attrs([('href', '#')])
+
+ To add a new attribute, use the ``|`` operator, where the right hand value
+ is a sequence of ``(name, value)`` tuples (which includes `Attrs`
+ instances):
+
+ >>> attrs | [('title', 'Bar')]
+ Attrs([('href', '#'), ('title', 'Bar')])
+
+ If the attributes already contain an attribute with a given name, the value
+ of that attribute is replaced:
+
+ >>> attrs | [('href', 'http://example.org/')]
+ Attrs([('href', 'http://example.org/')])
+ """
+ __slots__ = []
+
+ def __contains__(self, name):
+ """Return whether the list includes an attribute with the specified
+ name.
+
+ :return: `True` if the list includes the attribute
+ :rtype: `bool`
+ """
+ for attr, _ in self:
+ if attr == name:
+ return True
+
+ def __getitem__(self, i):
+ """Return an item or slice of the attributes list.
+
+ >>> attrs = Attrs([('href', '#'), ('title', 'Foo')])
+ >>> attrs[1]
+ ('title', 'Foo')
+ >>> attrs[1:]
+ Attrs([('title', 'Foo')])
+ """
+ items = tuple.__getitem__(self, i)
+ if type(i) is slice:
+ return Attrs(items)
+ return items
+
+ def __getslice__(self, i, j):
+ """Return a slice of the attributes list.
+
+ >>> attrs = Attrs([('href', '#'), ('title', 'Foo')])
+ >>> attrs[1:]
+ Attrs([('title', 'Foo')])
+ """
+ return Attrs(tuple.__getslice__(self, i, j))
+
+ def __or__(self, attrs):
+ """Return a new instance that contains the attributes in `attrs` in
+ addition to any already existing attributes.
+
+ :return: a new instance with the merged attributes
+ :rtype: `Attrs`
+ """
+ repl = dict([(an, av) for an, av in attrs if an in self])
+ return Attrs([(sn, repl.get(sn, sv)) for sn, sv in self] +
+ [(an, av) for an, av in attrs if an not in self])
+
+ def __repr__(self):
+ if not self:
+ return 'Attrs()'
+ return 'Attrs([%s])' % ', '.join([repr(item) for item in self])
+
+ def __sub__(self, names):
+ """Return a new instance with all attributes with a name in `names` are
+ removed.
+
+ :param names: the names of the attributes to remove
+ :return: a new instance with the attribute removed
+ :rtype: `Attrs`
+ """
+ if isinstance(names, basestring):
+ names = (names,)
+ return Attrs([(name, val) for name, val in self if name not in names])
+
+ def get(self, name, default=None):
+ """Return the value of the attribute with the specified name, or the
+ value of the `default` parameter if no such attribute is found.
+
+ :param name: the name of the attribute
+ :param default: the value to return when the attribute does not exist
+ :return: the attribute value, or the `default` value if that attribute
+ does not exist
+ :rtype: `object`
+ """
+ for attr, value in self:
+ if attr == name:
+ return value
+ return default
+
+ def totuple(self):
+ """Return the attributes as a markup event.
+
+ The returned event is a `TEXT` event, the data is the value of all
+ attributes joined together.
+
+ >>> Attrs([('href', '#'), ('title', 'Foo')]).totuple()
+ ('TEXT', '#Foo', (None, -1, -1))
+
+ :return: a `TEXT` event
+ :rtype: `tuple`
+ """
+ return TEXT, ''.join([x[1] for x in self]), (None, -1, -1)
+
+
+class Markup(unicode):
+ """Marks a string as being safe for inclusion in HTML/XML output without
+ needing to be escaped.
+ """
+ __slots__ = []
+
+ def __add__(self, other):
+ return Markup(unicode.__add__(self, escape(other)))
+
+ def __radd__(self, other):
+ return Markup(unicode.__add__(escape(other), self))
+
+ def __mod__(self, args):
+ if isinstance(args, dict):
+ args = dict(zip(args.keys(), map(escape, args.values())))
+ elif isinstance(args, (list, tuple)):
+ args = tuple(map(escape, args))
+ else:
+ args = escape(args)
+ return Markup(unicode.__mod__(self, args))
+
+ def __mul__(self, num):
+ return Markup(unicode.__mul__(self, num))
+ __rmul__ = __mul__
+
+ def __repr__(self):
+ return "<%s %s>" % (type(self).__name__, unicode.__repr__(self))
+
+ def join(self, seq, escape_quotes=True):
+ """Return a `Markup` object which is the concatenation of the strings
+ in the given sequence, where this `Markup` object is the separator
+ between the joined elements.
+
+ Any element in the sequence that is not a `Markup` instance is
+ automatically escaped.
+
+ :param seq: the sequence of strings to join
+ :param escape_quotes: whether double quote characters in the elements
+ should be escaped
+ :return: the joined `Markup` object
+ :rtype: `Markup`
+ :see: `escape`
+ """
+ return Markup(unicode.join(self, [escape(item, quotes=escape_quotes)
+ for item in seq]))
+
+ @classmethod
+ def escape(cls, text, quotes=True):
+ """Create a Markup instance from a string and escape special characters
+ it may contain (<, >, & and \").
+
+ >>> escape('"1 < 2"')
+ <Markup u'&#34;1 &lt; 2&#34;'>
+
+ If the `quotes` parameter is set to `False`, the \" character is left
+ as is. Escaping quotes is generally only required for strings that are
+ to be used in attribute values.
+
+ >>> escape('"1 < 2"', quotes=False)
+ <Markup u'"1 &lt; 2"'>
+
+ :param text: the text to escape
+ :param quotes: if ``True``, double quote characters are escaped in
+ addition to the other special characters
+ :return: the escaped `Markup` string
+ :rtype: `Markup`
+ """
+ if not text:
+ return cls()
+ if type(text) is cls:
+ return text
+ if hasattr(text, '__html__'):
+ return Markup(text.__html__())
+
+ text = text.replace('&', '&amp;') \
+ .replace('<', '&lt;') \
+ .replace('>', '&gt;')
+ if quotes:
+ text = text.replace('"', '&#34;')
+ return cls(text)
+
+ def unescape(self):
+ """Reverse-escapes &, <, >, and \" and returns a `unicode` object.
+
+ >>> Markup('1 &lt; 2').unescape()
+ u'1 < 2'
+
+ :return: the unescaped string
+ :rtype: `unicode`
+ :see: `genshi.core.unescape`
+ """
+ if not self:
+ return ''
+ return unicode(self).replace('&#34;', '"') \
+ .replace('&gt;', '>') \
+ .replace('&lt;', '<') \
+ .replace('&amp;', '&')
+
+ def stripentities(self, keepxmlentities=False):
+ """Return a copy of the text with any character or numeric entities
+ replaced by the equivalent UTF-8 characters.
+
+ If the `keepxmlentities` parameter is provided and evaluates to `True`,
+ the core XML entities (``&amp;``, ``&apos;``, ``&gt;``, ``&lt;`` and
+ ``&quot;``) are not stripped.
+
+ :return: a `Markup` instance with entities removed
+ :rtype: `Markup`
+ :see: `genshi.util.stripentities`
+ """
+ return Markup(stripentities(self, keepxmlentities=keepxmlentities))
+
+ def striptags(self):
+ """Return a copy of the text with all XML/HTML tags removed.
+
+ :return: a `Markup` instance with all tags removed
+ :rtype: `Markup`
+ :see: `genshi.util.striptags`
+ """
+ return Markup(striptags(self))
+
+
+try:
+ from genshi._speedups import Markup
+except ImportError:
+ pass # just use the Python implementation
+
+
+escape = Markup.escape
+
+
+def unescape(text):
+ """Reverse-escapes &, <, >, and \" and returns a `unicode` object.
+
+ >>> unescape(Markup('1 &lt; 2'))
+ u'1 < 2'
+
+ If the provided `text` object is not a `Markup` instance, it is returned
+ unchanged.
+
+ >>> unescape('1 &lt; 2')
+ '1 &lt; 2'
+
+ :param text: the text to unescape
+ :return: the unescsaped string
+ :rtype: `unicode`
+ """
+ if not isinstance(text, Markup):
+ return text
+ return text.unescape()
+
+
+class Namespace(object):
+ """Utility class creating and testing elements with a namespace.
+
+ Internally, namespace URIs are encoded in the `QName` of any element or
+ attribute, the namespace URI being enclosed in curly braces. This class
+ helps create and test these strings.
+
+ A `Namespace` object is instantiated with the namespace URI.
+
+ >>> html = Namespace('http://www.w3.org/1999/xhtml')
+ >>> html
+ Namespace('http://www.w3.org/1999/xhtml')
+ >>> html.uri
+ u'http://www.w3.org/1999/xhtml'
+
+ The `Namespace` object can than be used to generate `QName` objects with
+ that namespace:
+
+ >>> html.body
+ QName('http://www.w3.org/1999/xhtml}body')
+ >>> html.body.localname
+ u'body'
+ >>> html.body.namespace
+ u'http://www.w3.org/1999/xhtml'
+
+ The same works using item access notation, which is useful for element or
+ attribute names that are not valid Python identifiers:
+
+ >>> html['body']
+ QName('http://www.w3.org/1999/xhtml}body')
+
+ A `Namespace` object can also be used to test whether a specific `QName`
+ belongs to that namespace using the ``in`` operator:
+
+ >>> qname = html.body
+ >>> qname in html
+ True
+ >>> qname in Namespace('http://www.w3.org/2002/06/xhtml2')
+ False
+ """
+ def __new__(cls, uri):
+ if type(uri) is cls:
+ return uri
+ return object.__new__(cls)
+
+ def __getnewargs__(self):
+ return (self.uri,)
+
+ def __getstate__(self):
+ return self.uri
+
+ def __setstate__(self, uri):
+ self.uri = uri
+
+ def __init__(self, uri):
+ self.uri = unicode(uri)
+
+ def __contains__(self, qname):
+ return qname.namespace == self.uri
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __eq__(self, other):
+ if isinstance(other, Namespace):
+ return self.uri == other.uri
+ return self.uri == other
+
+ def __getitem__(self, name):
+ return QName(self.uri + '}' + name)
+ __getattr__ = __getitem__
+
+ def __hash__(self):
+ return hash(self.uri)
+
+ def __repr__(self):
+ return '%s(%s)' % (type(self).__name__, stringrepr(self.uri))
+
+ def __str__(self):
+ return self.uri.encode('utf-8')
+
+ def __unicode__(self):
+ return self.uri
+
+
+# The namespace used by attributes such as xml:lang and xml:space
+XML_NAMESPACE = Namespace('http://www.w3.org/XML/1998/namespace')
+
+
+class QName(unicode):
+ """A qualified element or attribute name.
+
+ The unicode value of instances of this class contains the qualified name of
+ the element or attribute, in the form ``{namespace-uri}local-name``. The
+ namespace URI can be obtained through the additional `namespace` attribute,
+ while the local name can be accessed through the `localname` attribute.
+
+ >>> qname = QName('foo')
+ >>> qname
+ QName('foo')
+ >>> qname.localname
+ u'foo'
+ >>> qname.namespace
+
+ >>> qname = QName('http://www.w3.org/1999/xhtml}body')
+ >>> qname
+ QName('http://www.w3.org/1999/xhtml}body')
+ >>> qname.localname
+ u'body'
+ >>> qname.namespace
+ u'http://www.w3.org/1999/xhtml'
+ """
+ __slots__ = ['namespace', 'localname']
+
+ def __new__(cls, qname):
+ """Create the `QName` instance.
+
+ :param qname: the qualified name as a string of the form
+ ``{namespace-uri}local-name``, where the leading curly
+ brace is optional
+ """
+ if type(qname) is cls:
+ return qname
+
+ parts = qname.lstrip('{').split('}', 1)
+ if len(parts) > 1:
+ self = unicode.__new__(cls, '{%s' % qname)
+ self.namespace, self.localname = map(unicode, parts)
+ else:
+ self = unicode.__new__(cls, qname)
+ self.namespace, self.localname = None, unicode(qname)
+ return self
+
+ def __getnewargs__(self):
+ return (self.lstrip('{'),)
+
+ def __repr__(self):
+ return '%s(%s)' % (type(self).__name__, stringrepr(self.lstrip('{')))
diff --git a/websdk/genshi/filters/__init__.py b/websdk/genshi/filters/__init__.py
new file mode 100644
index 0000000..efc2565
--- /dev/null
+++ b/websdk/genshi/filters/__init__.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Implementation of a number of stream filters."""
+
+from genshi.filters.html import HTMLFormFiller, HTMLSanitizer
+from genshi.filters.i18n import Translator
+from genshi.filters.transform import Transformer
+
+__docformat__ = 'restructuredtext en'
diff --git a/websdk/genshi/filters/html.py b/websdk/genshi/filters/html.py
new file mode 100644
index 0000000..d554a54
--- /dev/null
+++ b/websdk/genshi/filters/html.py
@@ -0,0 +1,453 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Implementation of a number of stream filters."""
+
+try:
+ any
+except NameError:
+ from genshi.util import any
+import re
+
+from genshi.core import Attrs, QName, stripentities
+from genshi.core import END, START, TEXT, COMMENT
+
+__all__ = ['HTMLFormFiller', 'HTMLSanitizer']
+__docformat__ = 'restructuredtext en'
+
+
+class HTMLFormFiller(object):
+ """A stream filter that can populate HTML forms from a dictionary of values.
+
+ >>> from genshi.input import HTML
+ >>> html = HTML('''<form>
+ ... <p><input type="text" name="foo" /></p>
+ ... </form>''')
+ >>> filler = HTMLFormFiller(data={'foo': 'bar'})
+ >>> print(html | filler)
+ <form>
+ <p><input type="text" name="foo" value="bar"/></p>
+ </form>
+ """
+ # TODO: only select the first radio button, and the first select option
+ # (if not in a multiple-select)
+ # TODO: only apply to elements in the XHTML namespace (or no namespace)?
+
+ def __init__(self, name=None, id=None, data=None, passwords=False):
+ """Create the filter.
+
+ :param name: The name of the form that should be populated. If this
+ parameter is given, only forms where the ``name`` attribute
+ value matches the parameter are processed.
+ :param id: The ID of the form that should be populated. If this
+ parameter is given, only forms where the ``id`` attribute
+ value matches the parameter are processed.
+ :param data: The dictionary of form values, where the keys are the names
+ of the form fields, and the values are the values to fill
+ in.
+ :param passwords: Whether password input fields should be populated.
+ This is off by default for security reasons (for
+ example, a password may end up in the browser cache)
+ :note: Changed in 0.5.2: added the `passwords` option
+ """
+ self.name = name
+ self.id = id
+ if data is None:
+ data = {}
+ self.data = data
+ self.passwords = passwords
+
+ def __call__(self, stream):
+ """Apply the filter to the given stream.
+
+ :param stream: the markup event stream to filter
+ """
+ in_form = in_select = in_option = in_textarea = False
+ select_value = option_value = textarea_value = None
+ option_start = None
+ option_text = []
+ no_option_value = False
+
+ for kind, data, pos in stream:
+
+ if kind is START:
+ tag, attrs = data
+ tagname = tag.localname
+
+ if tagname == 'form' and (
+ self.name and attrs.get('name') == self.name or
+ self.id and attrs.get('id') == self.id or
+ not (self.id or self.name)):
+ in_form = True
+
+ elif in_form:
+ if tagname == 'input':
+ type = attrs.get('type', '').lower()
+ if type in ('checkbox', 'radio'):
+ name = attrs.get('name')
+ if name and name in self.data:
+ value = self.data[name]
+ declval = attrs.get('value')
+ checked = False
+ if isinstance(value, (list, tuple)):
+ if declval:
+ checked = declval in [unicode(v) for v
+ in value]
+ else:
+ checked = any(value)
+ else:
+ if declval:
+ checked = declval == unicode(value)
+ elif type == 'checkbox':
+ checked = bool(value)
+ if checked:
+ attrs |= [(QName('checked'), 'checked')]
+ elif 'checked' in attrs:
+ attrs -= 'checked'
+ elif type in ('', 'hidden', 'text') \
+ or type == 'password' and self.passwords:
+ name = attrs.get('name')
+ if name and name in self.data:
+ value = self.data[name]
+ if isinstance(value, (list, tuple)):
+ value = value[0]
+ if value is not None:
+ attrs |= [
+ (QName('value'), unicode(value))
+ ]
+ elif tagname == 'select':
+ name = attrs.get('name')
+ if name in self.data:
+ select_value = self.data[name]
+ in_select = True
+ elif tagname == 'textarea':
+ name = attrs.get('name')
+ if name in self.data:
+ textarea_value = self.data.get(name)
+ if isinstance(textarea_value, (list, tuple)):
+ textarea_value = textarea_value[0]
+ in_textarea = True
+ elif in_select and tagname == 'option':
+ option_start = kind, data, pos
+ option_value = attrs.get('value')
+ if option_value is None:
+ no_option_value = True
+ option_value = ''
+ in_option = True
+ continue
+ yield kind, (tag, attrs), pos
+
+ elif in_form and kind is TEXT:
+ if in_select and in_option:
+ if no_option_value:
+ option_value += data
+ option_text.append((kind, data, pos))
+ continue
+ elif in_textarea:
+ continue
+ yield kind, data, pos
+
+ elif in_form and kind is END:
+ tagname = data.localname
+ if tagname == 'form':
+ in_form = False
+ elif tagname == 'select':
+ in_select = False
+ select_value = None
+ elif in_select and tagname == 'option':
+ if isinstance(select_value, (tuple, list)):
+ selected = option_value in [unicode(v) for v
+ in select_value]
+ else:
+ selected = option_value == unicode(select_value)
+ okind, (tag, attrs), opos = option_start
+ if selected:
+ attrs |= [(QName('selected'), 'selected')]
+ elif 'selected' in attrs:
+ attrs -= 'selected'
+ yield okind, (tag, attrs), opos
+ if option_text:
+ for event in option_text:
+ yield event
+ in_option = False
+ no_option_value = False
+ option_start = option_value = None
+ option_text = []
+ elif tagname == 'textarea':
+ if textarea_value:
+ yield TEXT, unicode(textarea_value), pos
+ in_textarea = False
+ yield kind, data, pos
+
+ else:
+ yield kind, data, pos
+
+
+class HTMLSanitizer(object):
+ """A filter that removes potentially dangerous HTML tags and attributes
+ from the stream.
+
+ >>> from genshi import HTML
+ >>> html = HTML('<div><script>alert(document.cookie)</script></div>')
+ >>> print(html | HTMLSanitizer())
+ <div/>
+
+ The default set of safe tags and attributes can be modified when the filter
+ is instantiated. For example, to allow inline ``style`` attributes, the
+ following instantation would work:
+
+ >>> html = HTML('<div style="background: #000"></div>')
+ >>> sanitizer = HTMLSanitizer(safe_attrs=HTMLSanitizer.SAFE_ATTRS | set(['style']))
+ >>> print(html | sanitizer)
+ <div style="background: #000"/>
+
+ Note that even in this case, the filter *does* attempt to remove dangerous
+ constructs from style attributes:
+
+ >>> html = HTML('<div style="background: url(javascript:void); color: #000"></div>')
+ >>> print(html | sanitizer)
+ <div style="color: #000"/>
+
+ This handles HTML entities, unicode escapes in CSS and Javascript text, as
+ well as a lot of other things. However, the style tag is still excluded by
+ default because it is very hard for such sanitizing to be completely safe,
+ especially considering how much error recovery current web browsers perform.
+
+ It also does some basic filtering of CSS properties that may be used for
+ typical phishing attacks. For more sophisticated filtering, this class
+ provides a couple of hooks that can be overridden in sub-classes.
+
+ :warn: Note that this special processing of CSS is currently only applied to
+ style attributes, **not** style elements.
+ """
+
+ SAFE_TAGS = frozenset(['a', 'abbr', 'acronym', 'address', 'area', 'b',
+ 'big', 'blockquote', 'br', 'button', 'caption', 'center', 'cite',
+ 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt',
+ 'em', 'fieldset', 'font', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
+ 'hr', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'map',
+ 'menu', 'ol', 'optgroup', 'option', 'p', 'pre', 'q', 's', 'samp',
+ 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'table',
+ 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'tt', 'u',
+ 'ul', 'var'])
+
+ SAFE_ATTRS = frozenset(['abbr', 'accept', 'accept-charset', 'accesskey',
+ 'action', 'align', 'alt', 'axis', 'bgcolor', 'border', 'cellpadding',
+ 'cellspacing', 'char', 'charoff', 'charset', 'checked', 'cite', 'class',
+ 'clear', 'cols', 'colspan', 'color', 'compact', 'coords', 'datetime',
+ 'dir', 'disabled', 'enctype', 'for', 'frame', 'headers', 'height',
+ 'href', 'hreflang', 'hspace', 'id', 'ismap', 'label', 'lang',
+ 'longdesc', 'maxlength', 'media', 'method', 'multiple', 'name',
+ 'nohref', 'noshade', 'nowrap', 'prompt', 'readonly', 'rel', 'rev',
+ 'rows', 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size',
+ 'span', 'src', 'start', 'summary', 'tabindex', 'target', 'title',
+ 'type', 'usemap', 'valign', 'value', 'vspace', 'width'])
+
+ SAFE_SCHEMES = frozenset(['file', 'ftp', 'http', 'https', 'mailto', None])
+
+ URI_ATTRS = frozenset(['action', 'background', 'dynsrc', 'href', 'lowsrc',
+ 'src'])
+
+ def __init__(self, safe_tags=SAFE_TAGS, safe_attrs=SAFE_ATTRS,
+ safe_schemes=SAFE_SCHEMES, uri_attrs=URI_ATTRS):
+ """Create the sanitizer.
+
+ The exact set of allowed elements and attributes can be configured.
+
+ :param safe_tags: a set of tag names that are considered safe
+ :param safe_attrs: a set of attribute names that are considered safe
+ :param safe_schemes: a set of URI schemes that are considered safe
+ :param uri_attrs: a set of names of attributes that contain URIs
+ """
+ self.safe_tags = safe_tags
+ "The set of tag names that are considered safe."
+ self.safe_attrs = safe_attrs
+ "The set of attribute names that are considered safe."
+ self.uri_attrs = uri_attrs
+ "The set of names of attributes that may contain URIs."
+ self.safe_schemes = safe_schemes
+ "The set of URI schemes that are considered safe."
+
+ def __call__(self, stream):
+ """Apply the filter to the given stream.
+
+ :param stream: the markup event stream to filter
+ """
+ waiting_for = None
+
+ for kind, data, pos in stream:
+ if kind is START:
+ if waiting_for:
+ continue
+ tag, attrs = data
+ if not self.is_safe_elem(tag, attrs):
+ waiting_for = tag
+ continue
+
+ new_attrs = []
+ for attr, value in attrs:
+ value = stripentities(value)
+ if attr not in self.safe_attrs:
+ continue
+ elif attr in self.uri_attrs:
+ # Don't allow URI schemes such as "javascript:"
+ if not self.is_safe_uri(value):
+ continue
+ elif attr == 'style':
+ # Remove dangerous CSS declarations from inline styles
+ decls = self.sanitize_css(value)
+ if not decls:
+ continue
+ value = '; '.join(decls)
+ new_attrs.append((attr, value))
+
+ yield kind, (tag, Attrs(new_attrs)), pos
+
+ elif kind is END:
+ tag = data
+ if waiting_for:
+ if waiting_for == tag:
+ waiting_for = None
+ else:
+ yield kind, data, pos
+
+ elif kind is not COMMENT:
+ if not waiting_for:
+ yield kind, data, pos
+
+ def is_safe_css(self, propname, value):
+ """Determine whether the given css property declaration is to be
+ considered safe for inclusion in the output.
+
+ :param propname: the CSS property name
+ :param value: the value of the property
+ :return: whether the property value should be considered safe
+ :rtype: bool
+ :since: version 0.6
+ """
+ if propname == 'position':
+ return False
+ if propname.startswith('margin') and '-' in value:
+ # Negative margins can be used for phishing
+ return False
+ return True
+
+ def is_safe_elem(self, tag, attrs):
+ """Determine whether the given element should be considered safe for
+ inclusion in the output.
+
+ :param tag: the tag name of the element
+ :type tag: QName
+ :param attrs: the element attributes
+ :type attrs: Attrs
+ :return: whether the element should be considered safe
+ :rtype: bool
+ :since: version 0.6
+ """
+ if tag not in self.safe_tags:
+ return False
+ if tag.localname == 'input':
+ input_type = attrs.get('type', '').lower()
+ if input_type == 'password':
+ return False
+ return True
+
+ def is_safe_uri(self, uri):
+ """Determine whether the given URI is to be considered safe for
+ inclusion in the output.
+
+ The default implementation checks whether the scheme of the URI is in
+ the set of allowed URIs (`safe_schemes`).
+
+ >>> sanitizer = HTMLSanitizer()
+ >>> sanitizer.is_safe_uri('http://example.org/')
+ True
+ >>> sanitizer.is_safe_uri('javascript:alert(document.cookie)')
+ False
+
+ :param uri: the URI to check
+ :return: `True` if the URI can be considered safe, `False` otherwise
+ :rtype: `bool`
+ :since: version 0.4.3
+ """
+ if '#' in uri:
+ uri = uri.split('#', 1)[0] # Strip out the fragment identifier
+ if ':' not in uri:
+ return True # This is a relative URI
+ chars = [char for char in uri.split(':', 1)[0] if char.isalnum()]
+ return ''.join(chars).lower() in self.safe_schemes
+
+ def sanitize_css(self, text):
+ """Remove potentially dangerous property declarations from CSS code.
+
+ In particular, properties using the CSS ``url()`` function with a scheme
+ that is not considered safe are removed:
+
+ >>> sanitizer = HTMLSanitizer()
+ >>> sanitizer.sanitize_css(u'''
+ ... background: url(javascript:alert("foo"));
+ ... color: #000;
+ ... ''')
+ [u'color: #000']
+
+ Also, the proprietary Internet Explorer function ``expression()`` is
+ always stripped:
+
+ >>> sanitizer.sanitize_css(u'''
+ ... background: #fff;
+ ... color: #000;
+ ... width: e/**/xpression(alert("foo"));
+ ... ''')
+ [u'background: #fff', u'color: #000']
+
+ :param text: the CSS text; this is expected to be `unicode` and to not
+ contain any character or numeric references
+ :return: a list of declarations that are considered safe
+ :rtype: `list`
+ :since: version 0.4.3
+ """
+ decls = []
+ text = self._strip_css_comments(self._replace_unicode_escapes(text))
+ for decl in text.split(';'):
+ decl = decl.strip()
+ if not decl:
+ continue
+ try:
+ propname, value = decl.split(':', 1)
+ except ValueError:
+ continue
+ if not self.is_safe_css(propname.strip().lower(), value.strip()):
+ continue
+ is_evil = False
+ if 'expression' in value:
+ is_evil = True
+ for match in re.finditer(r'url\s*\(([^)]+)', value):
+ if not self.is_safe_uri(match.group(1)):
+ is_evil = True
+ break
+ if not is_evil:
+ decls.append(decl.strip())
+ return decls
+
+ _NORMALIZE_NEWLINES = re.compile(r'\r\n').sub
+ _UNICODE_ESCAPE = re.compile(r'\\([0-9a-fA-F]{1,6})\s?').sub
+
+ def _replace_unicode_escapes(self, text):
+ def _repl(match):
+ return unichr(int(match.group(1), 16))
+ return self._UNICODE_ESCAPE(_repl, self._NORMALIZE_NEWLINES('\n', text))
+
+ _CSS_COMMENTS = re.compile(r'/\*.*?\*/').sub
+
+ def _strip_css_comments(self, text):
+ return self._CSS_COMMENTS('', text)
diff --git a/websdk/genshi/filters/i18n.py b/websdk/genshi/filters/i18n.py
new file mode 100644
index 0000000..7852875
--- /dev/null
+++ b/websdk/genshi/filters/i18n.py
@@ -0,0 +1,1238 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007-2010 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Directives and utilities for internationalization and localization of
+templates.
+
+:since: version 0.4
+:note: Directives support added since version 0.6
+"""
+
+try:
+ any
+except NameError:
+ from genshi.util import any
+from gettext import NullTranslations
+import os
+import re
+from types import FunctionType
+
+from genshi.core import Attrs, Namespace, QName, START, END, TEXT, \
+ XML_NAMESPACE, _ensure, StreamEventKind
+from genshi.template.eval import _ast
+from genshi.template.base import DirectiveFactory, EXPR, SUB, _apply_directives
+from genshi.template.directives import Directive, StripDirective
+from genshi.template.markup import MarkupTemplate, EXEC
+
+__all__ = ['Translator', 'extract']
+__docformat__ = 'restructuredtext en'
+
+
+I18N_NAMESPACE = Namespace('http://genshi.edgewall.org/i18n')
+
+MSGBUF = StreamEventKind('MSGBUF')
+SUB_START = StreamEventKind('SUB_START')
+SUB_END = StreamEventKind('SUB_END')
+
+GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext', 'dgettext', 'dngettext',
+ 'ugettext', 'ungettext')
+
+
+class I18NDirective(Directive):
+ """Simple interface for i18n directives to support messages extraction."""
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ return _apply_directives(stream, directives, ctxt, vars)
+
+
+class ExtractableI18NDirective(I18NDirective):
+ """Simple interface for directives to support messages extraction."""
+
+ def extract(self, translator, stream, gettext_functions=GETTEXT_FUNCTIONS,
+ search_text=True, comment_stack=None):
+ raise NotImplementedError
+
+
+class CommentDirective(I18NDirective):
+ """Implementation of the ``i18n:comment`` template directive which adds
+ translation comments.
+
+ >>> tmpl = MarkupTemplate('''<html xmlns:i18n="http://genshi.edgewall.org/i18n">
+ ... <p i18n:comment="As in Foo Bar">Foo</p>
+ ... </html>''')
+ >>> translator = Translator()
+ >>> translator.setup(tmpl)
+ >>> list(translator.extract(tmpl.stream))
+ [(2, None, u'Foo', [u'As in Foo Bar'])]
+ """
+ __slots__ = ['comment']
+
+ def __init__(self, value, template=None, namespaces=None, lineno=-1,
+ offset=-1):
+ Directive.__init__(self, None, template, namespaces, lineno, offset)
+ self.comment = value
+
+
+class MsgDirective(ExtractableI18NDirective):
+ r"""Implementation of the ``i18n:msg`` directive which marks inner content
+ as translatable. Consider the following examples:
+
+ >>> tmpl = MarkupTemplate('''<html xmlns:i18n="http://genshi.edgewall.org/i18n">
+ ... <div i18n:msg="">
+ ... <p>Foo</p>
+ ... <p>Bar</p>
+ ... </div>
+ ... <p i18n:msg="">Foo <em>bar</em>!</p>
+ ... </html>''')
+
+ >>> translator = Translator()
+ >>> translator.setup(tmpl)
+ >>> list(translator.extract(tmpl.stream))
+ [(2, None, u'[1:Foo]\n [2:Bar]', []), (6, None, u'Foo [1:bar]!', [])]
+ >>> print(tmpl.generate().render())
+ <html>
+ <div><p>Foo</p>
+ <p>Bar</p></div>
+ <p>Foo <em>bar</em>!</p>
+ </html>
+
+ >>> tmpl = MarkupTemplate('''<html xmlns:i18n="http://genshi.edgewall.org/i18n">
+ ... <div i18n:msg="fname, lname">
+ ... <p>First Name: ${fname}</p>
+ ... <p>Last Name: ${lname}</p>
+ ... </div>
+ ... <p i18n:msg="">Foo <em>bar</em>!</p>
+ ... </html>''')
+ >>> translator.setup(tmpl)
+ >>> list(translator.extract(tmpl.stream)) #doctest: +NORMALIZE_WHITESPACE
+ [(2, None, u'[1:First Name: %(fname)s]\n [2:Last Name: %(lname)s]', []),
+ (6, None, u'Foo [1:bar]!', [])]
+
+ >>> tmpl = MarkupTemplate('''<html xmlns:i18n="http://genshi.edgewall.org/i18n">
+ ... <div i18n:msg="fname, lname">
+ ... <p>First Name: ${fname}</p>
+ ... <p>Last Name: ${lname}</p>
+ ... </div>
+ ... <p i18n:msg="">Foo <em>bar</em>!</p>
+ ... </html>''')
+ >>> translator.setup(tmpl)
+ >>> print(tmpl.generate(fname='John', lname='Doe').render())
+ <html>
+ <div><p>First Name: John</p>
+ <p>Last Name: Doe</p></div>
+ <p>Foo <em>bar</em>!</p>
+ </html>
+
+ Starting and ending white-space is stripped of to make it simpler for
+ translators. Stripping it is not that important since it's on the html
+ source, the rendered output will remain the same.
+ """
+ __slots__ = ['params', 'lineno']
+
+ def __init__(self, value, template=None, namespaces=None, lineno=-1,
+ offset=-1):
+ Directive.__init__(self, None, template, namespaces, lineno, offset)
+ self.params = [param.strip() for param in value.split(',') if param]
+ self.lineno = lineno
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ value = value.get('params', '').strip()
+ return super(MsgDirective, cls).attach(template, stream, value.strip(),
+ namespaces, pos)
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ gettext = ctxt.get('_i18n.gettext')
+ if ctxt.get('_i18n.domain'):
+ dgettext = ctxt.get('_i18n.dgettext')
+ assert hasattr(dgettext, '__call__'), \
+ 'No domain gettext function passed'
+ gettext = lambda msg: dgettext(ctxt.get('_i18n.domain'), msg)
+
+ def _generate():
+ msgbuf = MessageBuffer(self)
+ previous = stream.next()
+ if previous[0] is START:
+ yield previous
+ else:
+ msgbuf.append(*previous)
+ previous = stream.next()
+ for kind, data, pos in stream:
+ msgbuf.append(*previous)
+ previous = kind, data, pos
+ if previous[0] is not END:
+ msgbuf.append(*previous)
+ previous = None
+ for event in msgbuf.translate(gettext(msgbuf.format())):
+ yield event
+ if previous:
+ yield previous
+
+ return _apply_directives(_generate(), directives, ctxt, vars)
+
+ def extract(self, translator, stream, gettext_functions=GETTEXT_FUNCTIONS,
+ search_text=True, comment_stack=None):
+ msgbuf = MessageBuffer(self)
+ strip = False
+
+ stream = iter(stream)
+ previous = stream.next()
+ if previous[0] is START:
+ for message in translator._extract_attrs(previous,
+ gettext_functions,
+ search_text=search_text):
+ yield message
+ previous = stream.next()
+ strip = True
+ for event in stream:
+ if event[0] is START:
+ for message in translator._extract_attrs(event,
+ gettext_functions,
+ search_text=search_text):
+ yield message
+ msgbuf.append(*previous)
+ previous = event
+ if not strip:
+ msgbuf.append(*previous)
+
+ yield self.lineno, None, msgbuf.format(), comment_stack[-1:]
+
+
+class ChooseBranchDirective(I18NDirective):
+ __slots__ = ['params']
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ self.params = ctxt.get('_i18n.choose.params', [])[:]
+ msgbuf = MessageBuffer(self)
+ stream = _apply_directives(stream, directives, ctxt, vars)
+
+ previous = stream.next()
+ if previous[0] is START:
+ yield previous
+ else:
+ msgbuf.append(*previous)
+
+ try:
+ previous = stream.next()
+ except StopIteration:
+ # For example <i18n:singular> or <i18n:plural> directives
+ yield MSGBUF, (), -1 # the place holder for msgbuf output
+ ctxt['_i18n.choose.%s' % self.tagname] = msgbuf
+ return
+
+ for event in stream:
+ msgbuf.append(*previous)
+ previous = event
+ yield MSGBUF, (), -1 # the place holder for msgbuf output
+
+ if previous[0] is END:
+ yield previous # the outer end tag
+ else:
+ msgbuf.append(*previous)
+ ctxt['_i18n.choose.%s' % self.tagname] = msgbuf
+
+ def extract(self, translator, stream, gettext_functions=GETTEXT_FUNCTIONS,
+ search_text=True, comment_stack=None, msgbuf=None):
+ stream = iter(stream)
+ previous = stream.next()
+
+ if previous[0] is START:
+ # skip the enclosing element
+ for message in translator._extract_attrs(previous,
+ gettext_functions,
+ search_text=search_text):
+ yield message
+ previous = stream.next()
+
+ for event in stream:
+ if previous[0] is START:
+ for message in translator._extract_attrs(previous,
+ gettext_functions,
+ search_text=search_text):
+ yield message
+ msgbuf.append(*previous)
+ previous = event
+
+ if previous[0] is not END:
+ msgbuf.append(*previous)
+
+
+class SingularDirective(ChooseBranchDirective):
+ """Implementation of the ``i18n:singular`` directive to be used with the
+ ``i18n:choose`` directive."""
+
+
+class PluralDirective(ChooseBranchDirective):
+ """Implementation of the ``i18n:plural`` directive to be used with the
+ ``i18n:choose`` directive."""
+
+
+class ChooseDirective(ExtractableI18NDirective):
+ """Implementation of the ``i18n:choose`` directive which provides plural
+ internationalisation of strings.
+
+ This directive requires at least one parameter, the one which evaluates to
+ an integer which will allow to choose the plural/singular form. If you also
+ have expressions inside the singular and plural version of the string you
+ also need to pass a name for those parameters. Consider the following
+ examples:
+
+ >>> tmpl = MarkupTemplate('''\
+ <html xmlns:i18n="http://genshi.edgewall.org/i18n">
+ ... <div i18n:choose="num; num">
+ ... <p i18n:singular="">There is $num coin</p>
+ ... <p i18n:plural="">There are $num coins</p>
+ ... </div>
+ ... </html>''')
+ >>> translator = Translator()
+ >>> translator.setup(tmpl)
+ >>> list(translator.extract(tmpl.stream)) #doctest: +NORMALIZE_WHITESPACE
+ [(2, 'ngettext', (u'There is %(num)s coin',
+ u'There are %(num)s coins'), [])]
+
+ >>> tmpl = MarkupTemplate('''\
+ <html xmlns:i18n="http://genshi.edgewall.org/i18n">
+ ... <div i18n:choose="num; num">
+ ... <p i18n:singular="">There is $num coin</p>
+ ... <p i18n:plural="">There are $num coins</p>
+ ... </div>
+ ... </html>''')
+ >>> translator.setup(tmpl)
+ >>> print(tmpl.generate(num=1).render())
+ <html>
+ <div>
+ <p>There is 1 coin</p>
+ </div>
+ </html>
+ >>> print(tmpl.generate(num=2).render())
+ <html>
+ <div>
+ <p>There are 2 coins</p>
+ </div>
+ </html>
+
+ When used as a element and not as an attribute:
+
+ >>> tmpl = MarkupTemplate('''\
+ <html xmlns:i18n="http://genshi.edgewall.org/i18n">
+ ... <i18n:choose numeral="num" params="num">
+ ... <p i18n:singular="">There is $num coin</p>
+ ... <p i18n:plural="">There are $num coins</p>
+ ... </i18n:choose>
+ ... </html>''')
+ >>> translator.setup(tmpl)
+ >>> list(translator.extract(tmpl.stream)) #doctest: +NORMALIZE_WHITESPACE
+ [(2, 'ngettext', (u'There is %(num)s coin',
+ u'There are %(num)s coins'), [])]
+ """
+ __slots__ = ['numeral', 'params', 'lineno']
+
+ def __init__(self, value, template=None, namespaces=None, lineno=-1,
+ offset=-1):
+ Directive.__init__(self, None, template, namespaces, lineno, offset)
+ params = [v.strip() for v in value.split(';')]
+ self.numeral = self._parse_expr(params.pop(0), template, lineno, offset)
+ self.params = params and [name.strip() for name in
+ params[0].split(',') if name] or []
+ self.lineno = lineno
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ numeral = value.get('numeral', '').strip()
+ assert numeral is not '', "at least pass the numeral param"
+ params = [v.strip() for v in value.get('params', '').split(',')]
+ value = '%s; ' % numeral + ', '.join(params)
+ return super(ChooseDirective, cls).attach(template, stream, value,
+ namespaces, pos)
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ ctxt.push({'_i18n.choose.params': self.params,
+ '_i18n.choose.singular': None,
+ '_i18n.choose.plural': None})
+
+ ngettext = ctxt.get('_i18n.ngettext')
+ assert hasattr(ngettext, '__call__'), 'No ngettext function available'
+ dngettext = ctxt.get('_i18n.dngettext')
+ if not dngettext:
+ dngettext = lambda d, s, p, n: ngettext(s, p, n)
+
+ new_stream = []
+ singular_stream = None
+ singular_msgbuf = None
+ plural_stream = None
+ plural_msgbuf = None
+
+ numeral = self.numeral.evaluate(ctxt)
+ is_plural = self._is_plural(numeral, ngettext)
+
+ for event in stream:
+ if event[0] is SUB and any(isinstance(d, ChooseBranchDirective)
+ for d in event[1][0]):
+ subdirectives, substream = event[1]
+
+ if isinstance(subdirectives[0], SingularDirective):
+ singular_stream = list(_apply_directives(substream,
+ subdirectives,
+ ctxt, vars))
+ new_stream.append((MSGBUF, None, (None, -1, -1)))
+
+ elif isinstance(subdirectives[0], PluralDirective):
+ if is_plural:
+ plural_stream = list(_apply_directives(substream,
+ subdirectives,
+ ctxt, vars))
+
+ else:
+ new_stream.append(event)
+
+ if ctxt.get('_i18n.domain'):
+ ngettext = lambda s, p, n: dngettext(ctxt.get('_i18n.domain'),
+ s, p, n)
+
+ singular_msgbuf = ctxt.get('_i18n.choose.singular')
+ if is_plural:
+ plural_msgbuf = ctxt.get('_i18n.choose.plural')
+ msgbuf, choice = plural_msgbuf, plural_stream
+ else:
+ msgbuf, choice = singular_msgbuf, singular_stream
+ plural_msgbuf = MessageBuffer(self)
+
+ for kind, data, pos in new_stream:
+ if kind is MSGBUF:
+ for event in choice:
+ if event[0] is MSGBUF:
+ translation = ngettext(singular_msgbuf.format(),
+ plural_msgbuf.format(),
+ numeral)
+ for subevent in msgbuf.translate(translation):
+ yield subevent
+ else:
+ yield event
+ else:
+ yield kind, data, pos
+
+ ctxt.pop()
+
+ def extract(self, translator, stream, gettext_functions=GETTEXT_FUNCTIONS,
+ search_text=True, comment_stack=None):
+ strip = False
+ stream = iter(stream)
+ previous = stream.next()
+
+ if previous[0] is START:
+ # skip the enclosing element
+ for message in translator._extract_attrs(previous,
+ gettext_functions,
+ search_text=search_text):
+ yield message
+ previous = stream.next()
+ strip = True
+
+ singular_msgbuf = MessageBuffer(self)
+ plural_msgbuf = MessageBuffer(self)
+
+ for event in stream:
+ if previous[0] is SUB:
+ directives, substream = previous[1]
+ for directive in directives:
+ if isinstance(directive, SingularDirective):
+ for message in directive.extract(translator,
+ substream, gettext_functions, search_text,
+ comment_stack, msgbuf=singular_msgbuf):
+ yield message
+ elif isinstance(directive, PluralDirective):
+ for message in directive.extract(translator,
+ substream, gettext_functions, search_text,
+ comment_stack, msgbuf=plural_msgbuf):
+ yield message
+ elif not isinstance(directive, StripDirective):
+ singular_msgbuf.append(*previous)
+ plural_msgbuf.append(*previous)
+ else:
+ if previous[0] is START:
+ for message in translator._extract_attrs(previous,
+ gettext_functions,
+ search_text):
+ yield message
+ singular_msgbuf.append(*previous)
+ plural_msgbuf.append(*previous)
+ previous = event
+
+ if not strip:
+ singular_msgbuf.append(*previous)
+ plural_msgbuf.append(*previous)
+
+ yield self.lineno, 'ngettext', \
+ (singular_msgbuf.format(), plural_msgbuf.format()), \
+ comment_stack[-1:]
+
+ def _is_plural(self, numeral, ngettext):
+ # XXX: should we test which form was chosen like this!?!?!?
+ # There should be no match in any catalogue for these singular and
+ # plural test strings
+ singular = u'O\x85\xbe\xa9\xa8az\xc3?\xe6\xa1\x02n\x84\x93'
+ plural = u'\xcc\xfb+\xd3Pn\x9d\tT\xec\x1d\xda\x1a\x88\x00'
+ return ngettext(singular, plural, numeral) == plural
+
+
+class DomainDirective(I18NDirective):
+ """Implementation of the ``i18n:domain`` directive which allows choosing
+ another i18n domain(catalog) to translate from.
+
+ >>> from genshi.filters.tests.i18n import DummyTranslations
+ >>> tmpl = MarkupTemplate('''\
+ <html xmlns:i18n="http://genshi.edgewall.org/i18n">
+ ... <p i18n:msg="">Bar</p>
+ ... <div i18n:domain="foo">
+ ... <p i18n:msg="">FooBar</p>
+ ... <p>Bar</p>
+ ... <p i18n:domain="bar" i18n:msg="">Bar</p>
+ ... <p i18n:domain="">Bar</p>
+ ... </div>
+ ... <p>Bar</p>
+ ... </html>''')
+
+ >>> translations = DummyTranslations({'Bar': 'Voh'})
+ >>> translations.add_domain('foo', {'FooBar': 'BarFoo', 'Bar': 'foo_Bar'})
+ >>> translations.add_domain('bar', {'Bar': 'bar_Bar'})
+ >>> translator = Translator(translations)
+ >>> translator.setup(tmpl)
+
+ >>> print(tmpl.generate().render())
+ <html>
+ <p>Voh</p>
+ <div>
+ <p>BarFoo</p>
+ <p>foo_Bar</p>
+ <p>bar_Bar</p>
+ <p>Voh</p>
+ </div>
+ <p>Voh</p>
+ </html>
+ """
+ __slots__ = ['domain']
+
+ def __init__(self, value, template=None, namespaces=None, lineno=-1,
+ offset=-1):
+ Directive.__init__(self, None, template, namespaces, lineno, offset)
+ self.domain = value and value.strip() or '__DEFAULT__'
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ value = value.get('name')
+ return super(DomainDirective, cls).attach(template, stream, value,
+ namespaces, pos)
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ ctxt.push({'_i18n.domain': self.domain})
+ for event in _apply_directives(stream, directives, ctxt, vars):
+ yield event
+ ctxt.pop()
+
+
+class Translator(DirectiveFactory):
+ """Can extract and translate localizable strings from markup streams and
+ templates.
+
+ For example, assume the following template:
+
+ >>> tmpl = MarkupTemplate('''<html xmlns:py="http://genshi.edgewall.org/">
+ ... <head>
+ ... <title>Example</title>
+ ... </head>
+ ... <body>
+ ... <h1>Example</h1>
+ ... <p>${_("Hello, %(name)s") % dict(name=username)}</p>
+ ... </body>
+ ... </html>''', filename='example.html')
+
+ For demonstration, we define a dummy ``gettext``-style function with a
+ hard-coded translation table, and pass that to the `Translator` initializer:
+
+ >>> def pseudo_gettext(string):
+ ... return {
+ ... 'Example': 'Beispiel',
+ ... 'Hello, %(name)s': 'Hallo, %(name)s'
+ ... }[string]
+ >>> translator = Translator(pseudo_gettext)
+
+ Next, the translator needs to be prepended to any already defined filters
+ on the template:
+
+ >>> tmpl.filters.insert(0, translator)
+
+ When generating the template output, our hard-coded translations should be
+ applied as expected:
+
+ >>> print(tmpl.generate(username='Hans', _=pseudo_gettext))
+ <html>
+ <head>
+ <title>Beispiel</title>
+ </head>
+ <body>
+ <h1>Beispiel</h1>
+ <p>Hallo, Hans</p>
+ </body>
+ </html>
+
+ Note that elements defining ``xml:lang`` attributes that do not contain
+ variable expressions are ignored by this filter. That can be used to
+ exclude specific parts of a template from being extracted and translated.
+ """
+
+ directives = [
+ ('domain', DomainDirective),
+ ('comment', CommentDirective),
+ ('msg', MsgDirective),
+ ('choose', ChooseDirective),
+ ('singular', SingularDirective),
+ ('plural', PluralDirective)
+ ]
+
+ IGNORE_TAGS = frozenset([
+ QName('script'), QName('http://www.w3.org/1999/xhtml}script'),
+ QName('style'), QName('http://www.w3.org/1999/xhtml}style')
+ ])
+ INCLUDE_ATTRS = frozenset([
+ 'abbr', 'alt', 'label', 'prompt', 'standby', 'summary', 'title'
+ ])
+ NAMESPACE = I18N_NAMESPACE
+
+ def __init__(self, translate=NullTranslations(), ignore_tags=IGNORE_TAGS,
+ include_attrs=INCLUDE_ATTRS, extract_text=True):
+ """Initialize the translator.
+
+ :param translate: the translation function, for example ``gettext`` or
+ ``ugettext``.
+ :param ignore_tags: a set of tag names that should not be localized
+ :param include_attrs: a set of attribute names should be localized
+ :param extract_text: whether the content of text nodes should be
+ extracted, or only text in explicit ``gettext``
+ function calls
+
+ :note: Changed in 0.6: the `translate` parameter can now be either
+ a ``gettext``-style function, or an object compatible with the
+ ``NullTransalations`` or ``GNUTranslations`` interface
+ """
+ self.translate = translate
+ self.ignore_tags = ignore_tags
+ self.include_attrs = include_attrs
+ self.extract_text = extract_text
+
+ def __call__(self, stream, ctxt=None, translate_text=True,
+ translate_attrs=True):
+ """Translate any localizable strings in the given stream.
+
+ This function shouldn't be called directly. Instead, an instance of
+ the `Translator` class should be registered as a filter with the
+ `Template` or the `TemplateLoader`, or applied as a regular stream
+ filter. If used as a template filter, it should be inserted in front of
+ all the default filters.
+
+ :param stream: the markup event stream
+ :param ctxt: the template context (not used)
+ :param translate_text: whether text nodes should be translated (used
+ internally)
+ :param translate_attrs: whether attribute values should be translated
+ (used internally)
+ :return: the localized stream
+ """
+ ignore_tags = self.ignore_tags
+ include_attrs = self.include_attrs
+ skip = 0
+ xml_lang = XML_NAMESPACE['lang']
+ if not self.extract_text:
+ translate_text = False
+ translate_attrs = False
+
+ if type(self.translate) is FunctionType:
+ gettext = self.translate
+ if ctxt:
+ ctxt['_i18n.gettext'] = gettext
+ else:
+ gettext = self.translate.ugettext
+ ngettext = self.translate.ungettext
+ try:
+ dgettext = self.translate.dugettext
+ dngettext = self.translate.dungettext
+ except AttributeError:
+ dgettext = lambda _, y: gettext(y)
+ dngettext = lambda _, s, p, n: ngettext(s, p, n)
+ if ctxt:
+ ctxt['_i18n.gettext'] = gettext
+ ctxt['_i18n.ngettext'] = ngettext
+ ctxt['_i18n.dgettext'] = dgettext
+ ctxt['_i18n.dngettext'] = dngettext
+
+ if ctxt and ctxt.get('_i18n.domain'):
+ gettext = lambda msg: dgettext(ctxt.get('_i18n.domain'), msg)
+
+ for kind, data, pos in stream:
+
+ # skip chunks that should not be localized
+ if skip:
+ if kind is START:
+ skip += 1
+ elif kind is END:
+ skip -= 1
+ yield kind, data, pos
+ continue
+
+ # handle different events that can be localized
+ if kind is START:
+ tag, attrs = data
+ if tag in self.ignore_tags or \
+ isinstance(attrs.get(xml_lang), basestring):
+ skip += 1
+ yield kind, data, pos
+ continue
+
+ new_attrs = []
+ changed = False
+
+ for name, value in attrs:
+ newval = value
+ if isinstance(value, basestring):
+ if translate_attrs and name in include_attrs:
+ newval = gettext(value)
+ else:
+ newval = list(
+ self(_ensure(value), ctxt, translate_text=False)
+ )
+ if newval != value:
+ value = newval
+ changed = True
+ new_attrs.append((name, value))
+ if changed:
+ attrs = Attrs(new_attrs)
+
+ yield kind, (tag, attrs), pos
+
+ elif translate_text and kind is TEXT:
+ text = data.strip()
+ if text:
+ data = data.replace(text, unicode(gettext(text)))
+ yield kind, data, pos
+
+ elif kind is SUB:
+ directives, substream = data
+ current_domain = None
+ for idx, directive in enumerate(directives):
+ # Organize directives to make everything work
+ # FIXME: There's got to be a better way to do this!
+ if isinstance(directive, DomainDirective):
+ # Grab current domain and update context
+ current_domain = directive.domain
+ ctxt.push({'_i18n.domain': current_domain})
+ # Put domain directive as the first one in order to
+ # update context before any other directives evaluation
+ directives.insert(0, directives.pop(idx))
+
+ # If this is an i18n directive, no need to translate text
+ # nodes here
+ is_i18n_directive = any([
+ isinstance(d, ExtractableI18NDirective)
+ for d in directives
+ ])
+ substream = list(self(substream, ctxt,
+ translate_text=not is_i18n_directive,
+ translate_attrs=translate_attrs))
+ yield kind, (directives, substream), pos
+
+ if current_domain:
+ ctxt.pop()
+ else:
+ yield kind, data, pos
+
+ def extract(self, stream, gettext_functions=GETTEXT_FUNCTIONS,
+ search_text=True, comment_stack=None):
+ """Extract localizable strings from the given template stream.
+
+ For every string found, this function yields a ``(lineno, function,
+ message, comments)`` tuple, where:
+
+ * ``lineno`` is the number of the line on which the string was found,
+ * ``function`` is the name of the ``gettext`` function used (if the
+ string was extracted from embedded Python code), and
+ * ``message`` is the string itself (a ``unicode`` object, or a tuple
+ of ``unicode`` objects for functions with multiple string
+ arguments).
+ * ``comments`` is a list of comments related to the message, extracted
+ from ``i18n:comment`` attributes found in the markup
+
+ >>> tmpl = MarkupTemplate('''<html xmlns:py="http://genshi.edgewall.org/">
+ ... <head>
+ ... <title>Example</title>
+ ... </head>
+ ... <body>
+ ... <h1>Example</h1>
+ ... <p>${_("Hello, %(name)s") % dict(name=username)}</p>
+ ... <p>${ngettext("You have %d item", "You have %d items", num)}</p>
+ ... </body>
+ ... </html>''', filename='example.html')
+ >>> for line, func, msg, comments in Translator().extract(tmpl.stream):
+ ... print('%d, %r, %r' % (line, func, msg))
+ 3, None, u'Example'
+ 6, None, u'Example'
+ 7, '_', u'Hello, %(name)s'
+ 8, 'ngettext', (u'You have %d item', u'You have %d items', None)
+
+ :param stream: the event stream to extract strings from; can be a
+ regular stream or a template stream
+ :param gettext_functions: a sequence of function names that should be
+ treated as gettext-style localization
+ functions
+ :param search_text: whether the content of text nodes should be
+ extracted (used internally)
+
+ :note: Changed in 0.4.1: For a function with multiple string arguments
+ (such as ``ngettext``), a single item with a tuple of strings is
+ yielded, instead an item for each string argument.
+ :note: Changed in 0.6: The returned tuples now include a fourth
+ element, which is a list of comments for the translator.
+ """
+ if not self.extract_text:
+ search_text = False
+ if comment_stack is None:
+ comment_stack = []
+ skip = 0
+
+ xml_lang = XML_NAMESPACE['lang']
+
+ for kind, data, pos in stream:
+ if skip:
+ if kind is START:
+ skip += 1
+ if kind is END:
+ skip -= 1
+
+ if kind is START and not skip:
+ tag, attrs = data
+ if tag in self.ignore_tags or \
+ isinstance(attrs.get(xml_lang), basestring):
+ skip += 1
+ continue
+
+ for message in self._extract_attrs((kind, data, pos),
+ gettext_functions,
+ search_text=search_text):
+ yield message
+
+ elif not skip and search_text and kind is TEXT:
+ text = data.strip()
+ if text and [ch for ch in text if ch.isalpha()]:
+ yield pos[1], None, text, comment_stack[-1:]
+
+ elif kind is EXPR or kind is EXEC:
+ for funcname, strings in extract_from_code(data,
+ gettext_functions):
+ # XXX: Do we need to grab i18n:comment from comment_stack ???
+ yield pos[1], funcname, strings, []
+
+ elif kind is SUB:
+ directives, substream = data
+ in_comment = False
+
+ for idx, directive in enumerate(directives):
+ # Do a first loop to see if there's a comment directive
+ # If there is update context and pop it from directives
+ if isinstance(directive, CommentDirective):
+ in_comment = True
+ comment_stack.append(directive.comment)
+ if len(directives) == 1:
+ # in case we're in the presence of something like:
+ # <p i18n:comment="foo">Foo</p>
+ for message in self.extract(
+ substream, gettext_functions,
+ search_text=search_text and not skip,
+ comment_stack=comment_stack):
+ yield message
+ directives.pop(idx)
+ elif not isinstance(directive, I18NDirective):
+ # Remove all other non i18n directives from the process
+ directives.pop(idx)
+
+ if not directives and not in_comment:
+ # Extract content if there's no directives because
+ # strip was pop'ed and not because comment was pop'ed.
+ # Extraction in this case has been taken care of.
+ for message in self.extract(
+ substream, gettext_functions,
+ search_text=search_text and not skip):
+ yield message
+
+ for directive in directives:
+ if isinstance(directive, ExtractableI18NDirective):
+ for message in directive.extract(self,
+ substream, gettext_functions,
+ search_text=search_text and not skip,
+ comment_stack=comment_stack):
+ yield message
+ else:
+ for message in self.extract(
+ substream, gettext_functions,
+ search_text=search_text and not skip,
+ comment_stack=comment_stack):
+ yield message
+
+ if in_comment:
+ comment_stack.pop()
+
+ def get_directive_index(self, dir_cls):
+ total = len(self._dir_order)
+ if dir_cls in self._dir_order:
+ return self._dir_order.index(dir_cls) - total
+ return total
+
+ def setup(self, template):
+ """Convenience function to register the `Translator` filter and the
+ related directives with the given template.
+
+ :param template: a `Template` instance
+ """
+ template.filters.insert(0, self)
+ if hasattr(template, 'add_directives'):
+ template.add_directives(Translator.NAMESPACE, self)
+
+ def _extract_attrs(self, event, gettext_functions, search_text):
+ for name, value in event[1][1]:
+ if search_text and isinstance(value, basestring):
+ if name in self.include_attrs:
+ text = value.strip()
+ if text:
+ yield event[2][1], None, text, []
+ else:
+ for message in self.extract(_ensure(value), gettext_functions,
+ search_text=False):
+ yield message
+
+
+class MessageBuffer(object):
+ """Helper class for managing internationalized mixed content.
+
+ :since: version 0.5
+ """
+
+ def __init__(self, directive=None):
+ """Initialize the message buffer.
+
+ :param directive: the directive owning the buffer
+ :type directive: I18NDirective
+ """
+ # params list needs to be copied so that directives can be evaluated
+ # more than once
+ self.orig_params = self.params = directive.params[:]
+ self.directive = directive
+ self.string = []
+ self.events = {}
+ self.values = {}
+ self.depth = 1
+ self.order = 1
+ self.stack = [0]
+ self.subdirectives = {}
+
+ def append(self, kind, data, pos):
+ """Append a stream event to the buffer.
+
+ :param kind: the stream event kind
+ :param data: the event data
+ :param pos: the position of the event in the source
+ """
+ if kind is SUB:
+ # The order needs to be +1 because a new START kind event will
+ # happen and we we need to wrap those events into our custom kind(s)
+ order = self.stack[-1] + 1
+ subdirectives, substream = data
+ # Store the directives that should be applied after translation
+ self.subdirectives.setdefault(order, []).extend(subdirectives)
+ self.events.setdefault(order, []).append((SUB_START, None, pos))
+ for skind, sdata, spos in substream:
+ self.append(skind, sdata, spos)
+ self.events.setdefault(order, []).append((SUB_END, None, pos))
+ elif kind is TEXT:
+ if '[' in data or ']' in data:
+ # Quote [ and ] if it ain't us adding it, ie, if the user is
+ # using those chars in his templates, escape them
+ data = data.replace('[', '\[').replace(']', '\]')
+ self.string.append(data)
+ self.events.setdefault(self.stack[-1], []).append((kind, data, pos))
+ elif kind is EXPR:
+ if self.params:
+ param = self.params.pop(0)
+ else:
+ params = ', '.join(['"%s"' % p for p in self.orig_params if p])
+ if params:
+ params = "(%s)" % params
+ raise IndexError("%d parameters%s given to 'i18n:%s' but "
+ "%d or more expressions used in '%s', line %s"
+ % (len(self.orig_params), params,
+ self.directive.tagname,
+ len(self.orig_params) + 1,
+ os.path.basename(pos[0] or
+ 'In-memory Template'),
+ pos[1]))
+ self.string.append('%%(%s)s' % param)
+ self.events.setdefault(self.stack[-1], []).append((kind, data, pos))
+ self.values[param] = (kind, data, pos)
+ else:
+ if kind is START:
+ self.string.append('[%d:' % self.order)
+ self.stack.append(self.order)
+ self.events.setdefault(self.stack[-1],
+ []).append((kind, data, pos))
+ self.depth += 1
+ self.order += 1
+ elif kind is END:
+ self.depth -= 1
+ if self.depth:
+ self.events[self.stack[-1]].append((kind, data, pos))
+ self.string.append(']')
+ self.stack.pop()
+
+ def format(self):
+ """Return a message identifier representing the content in the
+ buffer.
+ """
+ return ''.join(self.string).strip()
+
+ def translate(self, string, regex=re.compile(r'%\((\w+)\)s')):
+ """Interpolate the given message translation with the events in the
+ buffer and return the translated stream.
+
+ :param string: the translated message string
+ """
+ substream = None
+
+ def yield_parts(string):
+ for idx, part in enumerate(regex.split(string)):
+ if idx % 2:
+ yield self.values[part]
+ elif part:
+ yield (TEXT,
+ part.replace('\[', '[').replace('\]', ']'),
+ (None, -1, -1)
+ )
+
+ parts = parse_msg(string)
+ parts_counter = {}
+ for order, string in parts:
+ parts_counter.setdefault(order, []).append(None)
+
+ while parts:
+ order, string = parts.pop(0)
+ if len(parts_counter[order]) == 1:
+ events = self.events[order]
+ else:
+ events = [self.events[order].pop(0)]
+ parts_counter[order].pop()
+
+ for event in events:
+ if event[0] is SUB_START:
+ substream = []
+ elif event[0] is SUB_END:
+ # Yield a substream which might have directives to be
+ # applied to it (after translation events)
+ yield SUB, (self.subdirectives[order], substream), event[2]
+ substream = None
+ elif event[0] is TEXT:
+ if string:
+ for part in yield_parts(string):
+ if substream is not None:
+ substream.append(part)
+ else:
+ yield part
+ # String handled, reset it
+ string = None
+ elif event[0] is START:
+ if substream is not None:
+ substream.append(event)
+ else:
+ yield event
+ if string:
+ for part in yield_parts(string):
+ if substream is not None:
+ substream.append(part)
+ else:
+ yield part
+ # String handled, reset it
+ string = None
+ elif event[0] is END:
+ if string:
+ for part in yield_parts(string):
+ if substream is not None:
+ substream.append(part)
+ else:
+ yield part
+ # String handled, reset it
+ string = None
+ if substream is not None:
+ substream.append(event)
+ else:
+ yield event
+ elif event[0] is EXPR:
+ # These are handled on the strings itself
+ continue
+ else:
+ if string:
+ for part in yield_parts(string):
+ if substream is not None:
+ substream.append(part)
+ else:
+ yield part
+ # String handled, reset it
+ string = None
+ if substream is not None:
+ substream.append(event)
+ else:
+ yield event
+
+
+def parse_msg(string, regex=re.compile(r'(?:\[(\d+)\:)|(?<!\\)\]')):
+ """Parse a translated message using Genshi mixed content message
+ formatting.
+
+ >>> parse_msg("See [1:Help].")
+ [(0, 'See '), (1, 'Help'), (0, '.')]
+
+ >>> parse_msg("See [1:our [2:Help] page] for details.")
+ [(0, 'See '), (1, 'our '), (2, 'Help'), (1, ' page'), (0, ' for details.')]
+
+ >>> parse_msg("[2:Details] finden Sie in [1:Hilfe].")
+ [(2, 'Details'), (0, ' finden Sie in '), (1, 'Hilfe'), (0, '.')]
+
+ >>> parse_msg("[1:] Bilder pro Seite anzeigen.")
+ [(1, ''), (0, ' Bilder pro Seite anzeigen.')]
+
+ :param string: the translated message string
+ :return: a list of ``(order, string)`` tuples
+ :rtype: `list`
+ """
+ parts = []
+ stack = [0]
+ while True:
+ mo = regex.search(string)
+ if not mo:
+ break
+
+ if mo.start() or stack[-1]:
+ parts.append((stack[-1], string[:mo.start()]))
+ string = string[mo.end():]
+
+ orderno = mo.group(1)
+ if orderno is not None:
+ stack.append(int(orderno))
+ else:
+ stack.pop()
+ if not stack:
+ break
+
+ if string:
+ parts.append((stack[-1], string))
+
+ return parts
+
+
+def extract_from_code(code, gettext_functions):
+ """Extract strings from Python bytecode.
+
+ >>> from genshi.template.eval import Expression
+ >>> expr = Expression('_("Hello")')
+ >>> list(extract_from_code(expr, GETTEXT_FUNCTIONS))
+ [('_', u'Hello')]
+
+ >>> expr = Expression('ngettext("You have %(num)s item", '
+ ... '"You have %(num)s items", num)')
+ >>> list(extract_from_code(expr, GETTEXT_FUNCTIONS))
+ [('ngettext', (u'You have %(num)s item', u'You have %(num)s items', None))]
+
+ :param code: the `Code` object
+ :type code: `genshi.template.eval.Code`
+ :param gettext_functions: a sequence of function names
+ :since: version 0.5
+ """
+ def _walk(node):
+ if isinstance(node, _ast.Call) and isinstance(node.func, _ast.Name) \
+ and node.func.id in gettext_functions:
+ strings = []
+ def _add(arg):
+ if isinstance(arg, _ast.Str) and isinstance(arg.s, basestring):
+ strings.append(unicode(arg.s, 'utf-8'))
+ elif arg:
+ strings.append(None)
+ [_add(arg) for arg in node.args]
+ _add(node.starargs)
+ _add(node.kwargs)
+ if len(strings) == 1:
+ strings = strings[0]
+ else:
+ strings = tuple(strings)
+ yield node.func.id, strings
+ elif node._fields:
+ children = []
+ for field in node._fields:
+ child = getattr(node, field, None)
+ if isinstance(child, list):
+ for elem in child:
+ children.append(elem)
+ elif isinstance(child, _ast.AST):
+ children.append(child)
+ for child in children:
+ for funcname, strings in _walk(child):
+ yield funcname, strings
+ return _walk(code.ast)
+
+
+def extract(fileobj, keywords, comment_tags, options):
+ """Babel extraction method for Genshi templates.
+
+ :param fileobj: the file-like object the messages should be extracted from
+ :param keywords: a list of keywords (i.e. function names) that should be
+ recognized as translation functions
+ :param comment_tags: a list of translator tags to search for and include
+ in the results
+ :param options: a dictionary of additional options (optional)
+ :return: an iterator over ``(lineno, funcname, message, comments)`` tuples
+ :rtype: ``iterator``
+ """
+ template_class = options.get('template_class', MarkupTemplate)
+ if isinstance(template_class, basestring):
+ module, clsname = template_class.split(':', 1)
+ template_class = getattr(__import__(module, {}, {}, [clsname]), clsname)
+ encoding = options.get('encoding', None)
+
+ extract_text = options.get('extract_text', True)
+ if isinstance(extract_text, basestring):
+ extract_text = extract_text.lower() in ('1', 'on', 'yes', 'true')
+
+ ignore_tags = options.get('ignore_tags', Translator.IGNORE_TAGS)
+ if isinstance(ignore_tags, basestring):
+ ignore_tags = ignore_tags.split()
+ ignore_tags = [QName(tag) for tag in ignore_tags]
+
+ include_attrs = options.get('include_attrs', Translator.INCLUDE_ATTRS)
+ if isinstance(include_attrs, basestring):
+ include_attrs = include_attrs.split()
+ include_attrs = [QName(attr) for attr in include_attrs]
+
+ tmpl = template_class(fileobj, filename=getattr(fileobj, 'name', None),
+ encoding=encoding)
+ tmpl.loader = None
+
+ translator = Translator(None, ignore_tags, include_attrs, extract_text)
+ if hasattr(tmpl, 'add_directives'):
+ tmpl.add_directives(Translator.NAMESPACE, translator)
+ for message in translator.extract(tmpl.stream, gettext_functions=keywords):
+ yield message
diff --git a/websdk/genshi/filters/transform.py b/websdk/genshi/filters/transform.py
new file mode 100644
index 0000000..9b75b06
--- /dev/null
+++ b/websdk/genshi/filters/transform.py
@@ -0,0 +1,1310 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""A filter for functional-style transformations of markup streams.
+
+The `Transformer` filter provides a variety of transformations that can be
+applied to parts of streams that match given XPath expressions. These
+transformations can be chained to achieve results that would be comparitively
+tedious to achieve by writing stream filters by hand. The approach of chaining
+node selection and transformation has been inspired by the `jQuery`_ Javascript
+library.
+
+ .. _`jQuery`: http://jquery.com/
+
+For example, the following transformation removes the ``<title>`` element from
+the ``<head>`` of the input document:
+
+>>> from genshi.builder import tag
+>>> html = HTML('''<html>
+... <head><title>Some Title</title></head>
+... <body>
+... Some <em>body</em> text.
+... </body>
+... </html>''')
+>>> print(html | Transformer('body/em').map(unicode.upper, TEXT)
+... .unwrap().wrap(tag.u))
+<html>
+ <head><title>Some Title</title></head>
+ <body>
+ Some <u>BODY</u> text.
+ </body>
+</html>
+
+The ``Transformer`` support a large number of useful transformations out of the
+box, but custom transformations can be added easily.
+
+:since: version 0.5
+"""
+
+import re
+import sys
+
+from genshi.builder import Element
+from genshi.core import Stream, Attrs, QName, TEXT, START, END, _ensure, Markup
+from genshi.path import Path
+
+__all__ = ['Transformer', 'StreamBuffer', 'InjectorTransformation', 'ENTER',
+ 'EXIT', 'INSIDE', 'OUTSIDE', 'BREAK']
+
+
+class TransformMark(str):
+ """A mark on a transformation stream."""
+ __slots__ = []
+ _instances = {}
+
+ def __new__(cls, val):
+ return cls._instances.setdefault(val, str.__new__(cls, val))
+
+
+ENTER = TransformMark('ENTER')
+"""Stream augmentation mark indicating that a selected element is being
+entered."""
+
+INSIDE = TransformMark('INSIDE')
+"""Stream augmentation mark indicating that processing is currently inside a
+selected element."""
+
+OUTSIDE = TransformMark('OUTSIDE')
+"""Stream augmentation mark indicating that a match occurred outside a selected
+element."""
+
+ATTR = TransformMark('ATTR')
+"""Stream augmentation mark indicating a selected element attribute."""
+
+EXIT = TransformMark('EXIT')
+"""Stream augmentation mark indicating that a selected element is being
+exited."""
+
+BREAK = TransformMark('BREAK')
+"""Stream augmentation mark indicating a break between two otherwise contiguous
+blocks of marked events.
+
+This is used primarily by the cut() transform to provide later transforms with
+an opportunity to operate on the cut buffer.
+"""
+
+
+class PushBackStream(object):
+ """Allows a single event to be pushed back onto the stream and re-consumed.
+ """
+ def __init__(self, stream):
+ self.stream = iter(stream)
+ self.peek = None
+
+ def push(self, event):
+ assert self.peek is None
+ self.peek = event
+
+ def __iter__(self):
+ while True:
+ if self.peek is not None:
+ peek = self.peek
+ self.peek = None
+ yield peek
+ else:
+ try:
+ event = self.stream.next()
+ yield event
+ except StopIteration:
+ if self.peek is None:
+ raise
+
+
+class Transformer(object):
+ """Stream filter that can apply a variety of different transformations to
+ a stream.
+
+ This is achieved by selecting the events to be transformed using XPath,
+ then applying the transformations to the events matched by the path
+ expression. Each marked event is in the form (mark, (kind, data, pos)),
+ where mark can be any of `ENTER`, `INSIDE`, `EXIT`, `OUTSIDE`, or `None`.
+
+ The first three marks match `START` and `END` events, and any events
+ contained `INSIDE` any selected XML/HTML element. A non-element match
+ outside a `START`/`END` container (e.g. ``text()``) will yield an `OUTSIDE`
+ mark.
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+
+ Transformations act on selected stream events matching an XPath expression.
+ Here's an example of removing some markup (the title, in this case)
+ selected by an expression:
+
+ >>> print(html | Transformer('head/title').remove())
+ <html><head/><body>Some <em>body</em> text.</body></html>
+
+ Inserted content can be passed in the form of a string, or a markup event
+ stream, which includes streams generated programmatically via the
+ `builder` module:
+
+ >>> from genshi.builder import tag
+ >>> print(html | Transformer('body').prepend(tag.h1('Document Title')))
+ <html><head><title>Some Title</title></head><body><h1>Document
+ Title</h1>Some <em>body</em> text.</body></html>
+
+ Each XPath expression determines the set of tags that will be acted upon by
+ subsequent transformations. In this example we select the ``<title>`` text,
+ copy it into a buffer, then select the ``<body>`` element and paste the
+ copied text into the body as ``<h1>`` enclosed text:
+
+ >>> buffer = StreamBuffer()
+ >>> print(html | Transformer('head/title/text()').copy(buffer)
+ ... .end().select('body').prepend(tag.h1(buffer)))
+ <html><head><title>Some Title</title></head><body><h1>Some Title</h1>Some
+ <em>body</em> text.</body></html>
+
+ Transformations can also be assigned and reused, although care must be
+ taken when using buffers, to ensure that buffers are cleared between
+ transforms:
+
+ >>> emphasis = Transformer('body//em').attr('class', 'emphasis')
+ >>> print(html | emphasis)
+ <html><head><title>Some Title</title></head><body>Some <em
+ class="emphasis">body</em> text.</body></html>
+ """
+
+ __slots__ = ['transforms']
+
+ def __init__(self, path='.'):
+ """Construct a new transformation filter.
+
+ :param path: an XPath expression (as string) or a `Path` instance
+ """
+ self.transforms = [SelectTransformation(path)]
+
+ def __call__(self, stream, keep_marks=False):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: the marked event stream to filter
+ :param keep_marks: Do not strip transformer selection marks from the
+ stream. Useful for testing.
+ :return: the transformed stream
+ :rtype: `Stream`
+ """
+ transforms = self._mark(stream)
+ for link in self.transforms:
+ transforms = link(transforms)
+ if not keep_marks:
+ transforms = self._unmark(transforms)
+ return Stream(transforms,
+ serializer=getattr(stream, 'serializer', None))
+
+ def apply(self, function):
+ """Apply a transformation to the stream.
+
+ Transformations can be chained, similar to stream filters. Any callable
+ accepting a marked stream can be used as a transform.
+
+ As an example, here is a simple `TEXT` event upper-casing transform:
+
+ >>> def upper(stream):
+ ... for mark, (kind, data, pos) in stream:
+ ... if mark and kind is TEXT:
+ ... yield mark, (kind, data.upper(), pos)
+ ... else:
+ ... yield mark, (kind, data, pos)
+ >>> short_stream = HTML('<body>Some <em>test</em> text</body>')
+ >>> print(short_stream | Transformer('.//em/text()').apply(upper))
+ <body>Some <em>TEST</em> text</body>
+ """
+ transformer = Transformer()
+ transformer.transforms = self.transforms[:]
+ if isinstance(function, Transformer):
+ transformer.transforms.extend(function.transforms)
+ else:
+ transformer.transforms.append(function)
+ return transformer
+
+ #{ Selection operations
+
+ def select(self, path):
+ """Mark events matching the given XPath expression, within the current
+ selection.
+
+ >>> html = HTML('<body>Some <em>test</em> text</body>')
+ >>> print(html | Transformer().select('.//em').trace())
+ (None, ('START', (QName('body'), Attrs()), (None, 1, 0)))
+ (None, ('TEXT', u'Some ', (None, 1, 6)))
+ ('ENTER', ('START', (QName('em'), Attrs()), (None, 1, 11)))
+ ('INSIDE', ('TEXT', u'test', (None, 1, 15)))
+ ('EXIT', ('END', QName('em'), (None, 1, 19)))
+ (None, ('TEXT', u' text', (None, 1, 24)))
+ (None, ('END', QName('body'), (None, 1, 29)))
+ <body>Some <em>test</em> text</body>
+
+ :param path: an XPath expression (as string) or a `Path` instance
+ :return: the stream augmented by transformation marks
+ :rtype: `Transformer`
+ """
+ return self.apply(SelectTransformation(path))
+
+ def invert(self):
+ """Invert selection so that marked events become unmarked, and vice
+ versa.
+
+ Specificaly, all marks are converted to null marks, and all null marks
+ are converted to OUTSIDE marks.
+
+ >>> html = HTML('<body>Some <em>test</em> text</body>')
+ >>> print(html | Transformer('//em').invert().trace())
+ ('OUTSIDE', ('START', (QName('body'), Attrs()), (None, 1, 0)))
+ ('OUTSIDE', ('TEXT', u'Some ', (None, 1, 6)))
+ (None, ('START', (QName('em'), Attrs()), (None, 1, 11)))
+ (None, ('TEXT', u'test', (None, 1, 15)))
+ (None, ('END', QName('em'), (None, 1, 19)))
+ ('OUTSIDE', ('TEXT', u' text', (None, 1, 24)))
+ ('OUTSIDE', ('END', QName('body'), (None, 1, 29)))
+ <body>Some <em>test</em> text</body>
+
+ :rtype: `Transformer`
+ """
+ return self.apply(InvertTransformation())
+
+ def end(self):
+ """End current selection, allowing all events to be selected.
+
+ Example:
+
+ >>> html = HTML('<body>Some <em>test</em> text</body>')
+ >>> print(html | Transformer('//em').end().trace())
+ ('OUTSIDE', ('START', (QName('body'), Attrs()), (None, 1, 0)))
+ ('OUTSIDE', ('TEXT', u'Some ', (None, 1, 6)))
+ ('OUTSIDE', ('START', (QName('em'), Attrs()), (None, 1, 11)))
+ ('OUTSIDE', ('TEXT', u'test', (None, 1, 15)))
+ ('OUTSIDE', ('END', QName('em'), (None, 1, 19)))
+ ('OUTSIDE', ('TEXT', u' text', (None, 1, 24)))
+ ('OUTSIDE', ('END', QName('body'), (None, 1, 29)))
+ <body>Some <em>test</em> text</body>
+
+ :return: the stream augmented by transformation marks
+ :rtype: `Transformer`
+ """
+ return self.apply(EndTransformation())
+
+ #{ Deletion operations
+
+ def empty(self):
+ """Empty selected elements of all content.
+
+ Example:
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('.//em').empty())
+ <html><head><title>Some Title</title></head><body>Some <em/>
+ text.</body></html>
+
+ :rtype: `Transformer`
+ """
+ return self.apply(EmptyTransformation())
+
+ def remove(self):
+ """Remove selection from the stream.
+
+ Example:
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('.//em').remove())
+ <html><head><title>Some Title</title></head><body>Some
+ text.</body></html>
+
+ :rtype: `Transformer`
+ """
+ return self.apply(RemoveTransformation())
+
+ #{ Direct element operations
+
+ def unwrap(self):
+ """Remove outermost enclosing elements from selection.
+
+ Example:
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('.//em').unwrap())
+ <html><head><title>Some Title</title></head><body>Some body
+ text.</body></html>
+
+ :rtype: `Transformer`
+ """
+ return self.apply(UnwrapTransformation())
+
+ def wrap(self, element):
+ """Wrap selection in an element.
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('.//em').wrap('strong'))
+ <html><head><title>Some Title</title></head><body>Some
+ <strong><em>body</em></strong> text.</body></html>
+
+ :param element: either a tag name (as string) or an `Element` object
+ :rtype: `Transformer`
+ """
+ return self.apply(WrapTransformation(element))
+
+ #{ Content insertion operations
+
+ def replace(self, content):
+ """Replace selection with content.
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('.//title/text()').replace('New Title'))
+ <html><head><title>New Title</title></head><body>Some <em>body</em>
+ text.</body></html>
+
+ :param content: Either a callable, an iterable of events, or a string
+ to insert.
+ :rtype: `Transformer`
+ """
+ return self.apply(ReplaceTransformation(content))
+
+ def before(self, content):
+ """Insert content before selection.
+
+ In this example we insert the word 'emphasised' before the <em> opening
+ tag:
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('.//em').before('emphasised '))
+ <html><head><title>Some Title</title></head><body>Some emphasised
+ <em>body</em> text.</body></html>
+
+ :param content: Either a callable, an iterable of events, or a string
+ to insert.
+ :rtype: `Transformer`
+ """
+ return self.apply(BeforeTransformation(content))
+
+ def after(self, content):
+ """Insert content after selection.
+
+ Here, we insert some text after the </em> closing tag:
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('.//em').after(' rock'))
+ <html><head><title>Some Title</title></head><body>Some <em>body</em>
+ rock text.</body></html>
+
+ :param content: Either a callable, an iterable of events, or a string
+ to insert.
+ :rtype: `Transformer`
+ """
+ return self.apply(AfterTransformation(content))
+
+ def prepend(self, content):
+ """Insert content after the ENTER event of the selection.
+
+ Inserting some new text at the start of the <body>:
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('.//body').prepend('Some new body text. '))
+ <html><head><title>Some Title</title></head><body>Some new body text.
+ Some <em>body</em> text.</body></html>
+
+ :param content: Either a callable, an iterable of events, or a string
+ to insert.
+ :rtype: `Transformer`
+ """
+ return self.apply(PrependTransformation(content))
+
+ def append(self, content):
+ """Insert content before the END event of the selection.
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('.//body').append(' Some new body text.'))
+ <html><head><title>Some Title</title></head><body>Some <em>body</em>
+ text. Some new body text.</body></html>
+
+ :param content: Either a callable, an iterable of events, or a string
+ to insert.
+ :rtype: `Transformer`
+ """
+ return self.apply(AppendTransformation(content))
+
+ #{ Attribute manipulation
+
+ def attr(self, name, value):
+ """Add, replace or delete an attribute on selected elements.
+
+ If `value` evaulates to `None` the attribute will be deleted from the
+ element:
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em class="before">body</em> <em>text</em>.</body>'
+ ... '</html>')
+ >>> print(html | Transformer('body/em').attr('class', None))
+ <html><head><title>Some Title</title></head><body>Some <em>body</em>
+ <em>text</em>.</body></html>
+
+ Otherwise the attribute will be set to `value`:
+
+ >>> print(html | Transformer('body/em').attr('class', 'emphasis'))
+ <html><head><title>Some Title</title></head><body>Some <em
+ class="emphasis">body</em> <em class="emphasis">text</em>.</body></html>
+
+ If `value` is a callable it will be called with the attribute name and
+ the `START` event for the matching element. Its return value will then
+ be used to set the attribute:
+
+ >>> def print_attr(name, event):
+ ... attrs = event[1][1]
+ ... print(attrs)
+ ... return attrs.get(name)
+ >>> print(html | Transformer('body/em').attr('class', print_attr))
+ Attrs([(QName('class'), u'before')])
+ Attrs()
+ <html><head><title>Some Title</title></head><body>Some <em
+ class="before">body</em> <em>text</em>.</body></html>
+
+ :param name: the name of the attribute
+ :param value: the value that should be set for the attribute.
+ :rtype: `Transformer`
+ """
+ return self.apply(AttrTransformation(name, value))
+
+ #{ Buffer operations
+
+ def copy(self, buffer, accumulate=False):
+ """Copy selection into buffer.
+
+ The buffer is replaced by each *contiguous* selection before being passed
+ to the next transformation. If accumulate=True, further selections will
+ be appended to the buffer rather than replacing it.
+
+ >>> from genshi.builder import tag
+ >>> buffer = StreamBuffer()
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('head/title/text()').copy(buffer)
+ ... .end().select('body').prepend(tag.h1(buffer)))
+ <html><head><title>Some Title</title></head><body><h1>Some
+ Title</h1>Some <em>body</em> text.</body></html>
+
+ This example illustrates that only a single contiguous selection will
+ be buffered:
+
+ >>> print(html | Transformer('head/title/text()').copy(buffer)
+ ... .end().select('body/em').copy(buffer).end().select('body')
+ ... .prepend(tag.h1(buffer)))
+ <html><head><title>Some Title</title></head><body><h1>Some
+ Title</h1>Some <em>body</em> text.</body></html>
+ >>> print(buffer)
+ <em>body</em>
+
+ Element attributes can also be copied for later use:
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body><em>Some</em> <em class="before">body</em>'
+ ... '<em>text</em>.</body></html>')
+ >>> buffer = StreamBuffer()
+ >>> def apply_attr(name, entry):
+ ... return list(buffer)[0][1][1].get('class')
+ >>> print(html | Transformer('body/em[@class]/@class').copy(buffer)
+ ... .end().buffer().select('body/em[not(@class)]')
+ ... .attr('class', apply_attr))
+ <html><head><title>Some Title</title></head><body><em
+ class="before">Some</em> <em class="before">body</em><em
+ class="before">text</em>.</body></html>
+
+
+ :param buffer: the `StreamBuffer` in which the selection should be
+ stored
+ :rtype: `Transformer`
+ :note: Copy (and cut) copy each individual selected object into the
+ buffer before passing to the next transform. For example, the
+ XPath ``*|text()`` will select all elements and text, each
+ instance of which will be copied to the buffer individually
+ before passing to the next transform. This has implications for
+ how ``StreamBuffer`` objects can be used, so some
+ experimentation may be required.
+
+ """
+ return self.apply(CopyTransformation(buffer, accumulate))
+
+ def cut(self, buffer, accumulate=False):
+ """Copy selection into buffer and remove the selection from the stream.
+
+ >>> from genshi.builder import tag
+ >>> buffer = StreamBuffer()
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('.//em/text()').cut(buffer)
+ ... .end().select('.//em').after(tag.h1(buffer)))
+ <html><head><title>Some Title</title></head><body>Some
+ <em/><h1>body</h1> text.</body></html>
+
+ Specifying accumulate=True, appends all selected intervals onto the
+ buffer. Combining this with the .buffer() operation allows us operate
+ on all copied events rather than per-segment. See the documentation on
+ buffer() for more information.
+
+ :param buffer: the `StreamBuffer` in which the selection should be
+ stored
+ :rtype: `Transformer`
+ :note: this transformation will buffer the entire input stream
+ """
+ return self.apply(CutTransformation(buffer, accumulate))
+
+ def buffer(self):
+ """Buffer the entire stream (can consume a considerable amount of
+ memory).
+
+ Useful in conjunction with copy(accumulate=True) and
+ cut(accumulate=True) to ensure that all marked events in the entire
+ stream are copied to the buffer before further transformations are
+ applied.
+
+ For example, to move all <note> elements inside a <notes> tag at the
+ top of the document:
+
+ >>> doc = HTML('<doc><notes></notes><body>Some <note>one</note> '
+ ... 'text <note>two</note>.</body></doc>')
+ >>> buffer = StreamBuffer()
+ >>> print(doc | Transformer('body/note').cut(buffer, accumulate=True)
+ ... .end().buffer().select('notes').prepend(buffer))
+ <doc><notes><note>one</note><note>two</note></notes><body>Some text
+ .</body></doc>
+
+ """
+ return self.apply(list)
+
+ #{ Miscellaneous operations
+
+ def filter(self, filter):
+ """Apply a normal stream filter to the selection. The filter is called
+ once for each contiguous block of marked events.
+
+ >>> from genshi.filters.html import HTMLSanitizer
+ >>> html = HTML('<html><body>Some text<script>alert(document.cookie)'
+ ... '</script> and some more text</body></html>')
+ >>> print(html | Transformer('body/*').filter(HTMLSanitizer()))
+ <html><body>Some text and some more text</body></html>
+
+ :param filter: The stream filter to apply.
+ :rtype: `Transformer`
+ """
+ return self.apply(FilterTransformation(filter))
+
+ def map(self, function, kind):
+ """Applies a function to the ``data`` element of events of ``kind`` in
+ the selection.
+
+ >>> html = HTML('<html><head><title>Some Title</title></head>'
+ ... '<body>Some <em>body</em> text.</body></html>')
+ >>> print(html | Transformer('head/title').map(unicode.upper, TEXT))
+ <html><head><title>SOME TITLE</title></head><body>Some <em>body</em>
+ text.</body></html>
+
+ :param function: the function to apply
+ :param kind: the kind of event the function should be applied to
+ :rtype: `Transformer`
+ """
+ return self.apply(MapTransformation(function, kind))
+
+ def substitute(self, pattern, replace, count=1):
+ """Replace text matching a regular expression.
+
+ Refer to the documentation for ``re.sub()`` for details.
+
+ >>> html = HTML('<html><body>Some text, some more text and '
+ ... '<b>some bold text</b>\\n'
+ ... '<i>some italicised text</i></body></html>')
+ >>> print(html | Transformer('body/b').substitute('(?i)some', 'SOME'))
+ <html><body>Some text, some more text and <b>SOME bold text</b>
+ <i>some italicised text</i></body></html>
+ >>> tags = tag.html(tag.body('Some text, some more text and\\n',
+ ... Markup('<b>some bold text</b>')))
+ >>> print(tags.generate() | Transformer('body').substitute(
+ ... '(?i)some', 'SOME'))
+ <html><body>SOME text, some more text and
+ <b>SOME bold text</b></body></html>
+
+ :param pattern: A regular expression object or string.
+ :param replace: Replacement pattern.
+ :param count: Number of replacements to make in each text fragment.
+ :rtype: `Transformer`
+ """
+ return self.apply(SubstituteTransformation(pattern, replace, count))
+
+ def rename(self, name):
+ """Rename matching elements.
+
+ >>> html = HTML('<html><body>Some text, some more text and '
+ ... '<b>some bold text</b></body></html>')
+ >>> print(html | Transformer('body/b').rename('strong'))
+ <html><body>Some text, some more text and <strong>some bold text</strong></body></html>
+ """
+ return self.apply(RenameTransformation(name))
+
+ def trace(self, prefix='', fileobj=None):
+ """Print events as they pass through the transform.
+
+ >>> html = HTML('<body>Some <em>test</em> text</body>')
+ >>> print(html | Transformer('em').trace())
+ (None, ('START', (QName('body'), Attrs()), (None, 1, 0)))
+ (None, ('TEXT', u'Some ', (None, 1, 6)))
+ ('ENTER', ('START', (QName('em'), Attrs()), (None, 1, 11)))
+ ('INSIDE', ('TEXT', u'test', (None, 1, 15)))
+ ('EXIT', ('END', QName('em'), (None, 1, 19)))
+ (None, ('TEXT', u' text', (None, 1, 24)))
+ (None, ('END', QName('body'), (None, 1, 29)))
+ <body>Some <em>test</em> text</body>
+
+ :param prefix: a string to prefix each event with in the output
+ :param fileobj: the writable file-like object to write to; defaults to
+ the standard output stream
+ :rtype: `Transformer`
+ """
+ return self.apply(TraceTransformation(prefix, fileobj=fileobj))
+
+ # Internal methods
+
+ def _mark(self, stream):
+ for event in stream:
+ yield OUTSIDE, event
+
+ def _unmark(self, stream):
+ for mark, event in stream:
+ kind = event[0]
+ if not (kind is None or kind is ATTR or kind is BREAK):
+ yield event
+
+
+class SelectTransformation(object):
+ """Select and mark events that match an XPath expression."""
+
+ def __init__(self, path):
+ """Create selection.
+
+ :param path: an XPath expression (as string) or a `Path` object
+ """
+ if not isinstance(path, Path):
+ path = Path(path)
+ self.path = path
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: the marked event stream to filter
+ """
+ namespaces = {}
+ variables = {}
+ test = self.path.test()
+ stream = iter(stream)
+ next = stream.next
+ for mark, event in stream:
+ if mark is None:
+ yield mark, event
+ continue
+ result = test(event, namespaces, variables)
+ # XXX This is effectively genshi.core._ensure() for transform
+ # streams.
+ if result is True:
+ if event[0] is START:
+ yield ENTER, event
+ depth = 1
+ while depth > 0:
+ mark, subevent = next()
+ if subevent[0] is START:
+ depth += 1
+ elif subevent[0] is END:
+ depth -= 1
+ if depth == 0:
+ yield EXIT, subevent
+ else:
+ yield INSIDE, subevent
+ test(subevent, namespaces, variables, updateonly=True)
+ else:
+ yield OUTSIDE, event
+ elif isinstance(result, Attrs):
+ # XXX Selected *attributes* are given a "kind" of None to
+ # indicate they are not really part of the stream.
+ yield ATTR, (ATTR, (QName(event[1][0] + '@*'), result), event[2])
+ yield None, event
+ elif isinstance(result, tuple):
+ yield OUTSIDE, result
+ elif result:
+ # XXX Assume everything else is "text"?
+ yield None, (TEXT, unicode(result), (None, -1, -1))
+ else:
+ yield None, event
+
+
+class InvertTransformation(object):
+ """Invert selection so that marked events become unmarked, and vice versa.
+
+ Specificaly, all input marks are converted to null marks, and all input
+ null marks are converted to OUTSIDE marks.
+ """
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: the marked event stream to filter
+ """
+ for mark, event in stream:
+ if mark:
+ yield None, event
+ else:
+ yield OUTSIDE, event
+
+
+class EndTransformation(object):
+ """End the current selection."""
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: the marked event stream to filter
+ """
+ for mark, event in stream:
+ yield OUTSIDE, event
+
+
+class EmptyTransformation(object):
+ """Empty selected elements of all content."""
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: the marked event stream to filter
+ """
+ for mark, event in stream:
+ yield mark, event
+ if mark is ENTER:
+ for mark, event in stream:
+ if mark is EXIT:
+ yield mark, event
+ break
+
+
+class RemoveTransformation(object):
+ """Remove selection from the stream."""
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: the marked event stream to filter
+ """
+ for mark, event in stream:
+ if mark is None:
+ yield mark, event
+
+
+class UnwrapTransformation(object):
+ """Remove outtermost enclosing elements from selection."""
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: the marked event stream to filter
+ """
+ for mark, event in stream:
+ if mark not in (ENTER, EXIT):
+ yield mark, event
+
+
+class WrapTransformation(object):
+ """Wrap selection in an element."""
+
+ def __init__(self, element):
+ if isinstance(element, Element):
+ self.element = element
+ else:
+ self.element = Element(element)
+
+ def __call__(self, stream):
+ for mark, event in stream:
+ if mark:
+ element = list(self.element.generate())
+ for prefix in element[:-1]:
+ yield None, prefix
+ yield mark, event
+ start = mark
+ stopped = False
+ for mark, event in stream:
+ if start is ENTER and mark is EXIT:
+ yield mark, event
+ stopped = True
+ break
+ if not mark:
+ break
+ yield mark, event
+ else:
+ stopped = True
+ yield None, element[-1]
+ if not stopped:
+ yield mark, event
+ else:
+ yield mark, event
+
+
+class TraceTransformation(object):
+ """Print events as they pass through the transform."""
+
+ def __init__(self, prefix='', fileobj=None):
+ """Trace constructor.
+
+ :param prefix: text to prefix each traced line with.
+ :param fileobj: the writable file-like object to write to
+ """
+ self.prefix = prefix
+ self.fileobj = fileobj or sys.stdout
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: the marked event stream to filter
+ """
+ for event in stream:
+ self.fileobj.write('%s%s\n' % (self.prefix, event))
+ yield event
+
+
+class FilterTransformation(object):
+ """Apply a normal stream filter to the selection. The filter is called once
+ for each selection."""
+
+ def __init__(self, filter):
+ """Create the transform.
+
+ :param filter: The stream filter to apply.
+ """
+ self.filter = filter
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: The marked event stream to filter
+ """
+ def flush(queue):
+ if queue:
+ for event in self.filter(queue):
+ yield OUTSIDE, event
+ del queue[:]
+
+ queue = []
+ for mark, event in stream:
+ if mark is ENTER:
+ queue.append(event)
+ for mark, event in stream:
+ queue.append(event)
+ if mark is EXIT:
+ break
+ for queue_event in flush(queue):
+ yield queue_event
+ elif mark is OUTSIDE:
+ stopped = False
+ queue.append(event)
+ for mark, event in stream:
+ if mark is not OUTSIDE:
+ break
+ queue.append(event)
+ else:
+ stopped = True
+ for queue_event in flush(queue):
+ yield queue_event
+ if not stopped:
+ yield mark, event
+ else:
+ yield mark, event
+ for queue_event in flush(queue):
+ yield queue_event
+
+
+class MapTransformation(object):
+ """Apply a function to the `data` element of events of ``kind`` in the
+ selection.
+ """
+
+ def __init__(self, function, kind):
+ """Create the transform.
+
+ :param function: the function to apply; the function must take one
+ argument, the `data` element of each selected event
+ :param kind: the stream event ``kind`` to apply the `function` to
+ """
+ self.function = function
+ self.kind = kind
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: The marked event stream to filter
+ """
+ for mark, (kind, data, pos) in stream:
+ if mark and self.kind in (None, kind):
+ yield mark, (kind, self.function(data), pos)
+ else:
+ yield mark, (kind, data, pos)
+
+
+class SubstituteTransformation(object):
+ """Replace text matching a regular expression.
+
+ Refer to the documentation for ``re.sub()`` for details.
+ """
+ def __init__(self, pattern, replace, count=0):
+ """Create the transform.
+
+ :param pattern: A regular expression object, or string.
+ :param replace: Replacement pattern.
+ :param count: Number of replacements to make in each text fragment.
+ """
+ if isinstance(pattern, basestring):
+ self.pattern = re.compile(pattern)
+ else:
+ self.pattern = pattern
+ self.count = count
+ self.replace = replace
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: The marked event stream to filter
+ """
+ for mark, (kind, data, pos) in stream:
+ if mark is not None and kind is TEXT:
+ new_data = self.pattern.sub(self.replace, data, self.count)
+ if isinstance(data, Markup):
+ data = Markup(new_data)
+ else:
+ data = new_data
+ yield mark, (kind, data, pos)
+
+
+class RenameTransformation(object):
+ """Rename matching elements."""
+ def __init__(self, name):
+ """Create the transform.
+
+ :param name: New element name.
+ """
+ self.name = QName(name)
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: The marked event stream to filter
+ """
+ for mark, (kind, data, pos) in stream:
+ if mark is ENTER:
+ data = self.name, data[1]
+ elif mark is EXIT:
+ data = self.name
+ yield mark, (kind, data, pos)
+
+
+class InjectorTransformation(object):
+ """Abstract base class for transformations that inject content into a
+ stream.
+
+ >>> class Top(InjectorTransformation):
+ ... def __call__(self, stream):
+ ... for event in self._inject():
+ ... yield event
+ ... for event in stream:
+ ... yield event
+ >>> html = HTML('<body>Some <em>test</em> text</body>')
+ >>> print(html | Transformer('.//em').apply(Top('Prefix ')))
+ Prefix <body>Some <em>test</em> text</body>
+ """
+ def __init__(self, content):
+ """Create a new injector.
+
+ :param content: An iterable of Genshi stream events, or a string to be
+ injected.
+ """
+ self.content = content
+
+ def _inject(self):
+ content = self.content
+ if hasattr(content, '__call__'):
+ content = content()
+ for event in _ensure(content):
+ yield None, event
+
+
+class ReplaceTransformation(InjectorTransformation):
+ """Replace selection with content."""
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: The marked event stream to filter
+ """
+ stream = PushBackStream(stream)
+ for mark, event in stream:
+ if mark is not None:
+ start = mark
+ for subevent in self._inject():
+ yield subevent
+ for mark, event in stream:
+ if start is ENTER:
+ if mark is EXIT:
+ break
+ elif mark != start:
+ stream.push((mark, event))
+ break
+ else:
+ yield mark, event
+
+
+class BeforeTransformation(InjectorTransformation):
+ """Insert content before selection."""
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: The marked event stream to filter
+ """
+ stream = PushBackStream(stream)
+ for mark, event in stream:
+ if mark is not None:
+ start = mark
+ for subevent in self._inject():
+ yield subevent
+ yield mark, event
+ for mark, event in stream:
+ if mark != start and start is not ENTER:
+ stream.push((mark, event))
+ break
+ yield mark, event
+ if start is ENTER and mark is EXIT:
+ break
+ else:
+ yield mark, event
+
+
+class AfterTransformation(InjectorTransformation):
+ """Insert content after selection."""
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: The marked event stream to filter
+ """
+ stream = PushBackStream(stream)
+ for mark, event in stream:
+ yield mark, event
+ if mark:
+ start = mark
+ for mark, event in stream:
+ if start is not ENTER and mark != start:
+ stream.push((mark, event))
+ break
+ yield mark, event
+ if start is ENTER and mark is EXIT:
+ break
+ for subevent in self._inject():
+ yield subevent
+
+
+class PrependTransformation(InjectorTransformation):
+ """Prepend content to the inside of selected elements."""
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: The marked event stream to filter
+ """
+ for mark, event in stream:
+ yield mark, event
+ if mark is ENTER:
+ for subevent in self._inject():
+ yield subevent
+
+
+class AppendTransformation(InjectorTransformation):
+ """Append content after the content of selected elements."""
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: The marked event stream to filter
+ """
+ for mark, event in stream:
+ yield mark, event
+ if mark is ENTER:
+ for mark, event in stream:
+ if mark is EXIT:
+ break
+ yield mark, event
+ for subevent in self._inject():
+ yield subevent
+ yield mark, event
+
+
+class AttrTransformation(object):
+ """Set an attribute on selected elements."""
+
+ def __init__(self, name, value):
+ """Construct transform.
+
+ :param name: name of the attribute that should be set
+ :param value: the value to set
+ """
+ self.name = name
+ self.value = value
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: The marked event stream to filter
+ """
+ callable_value = hasattr(self.value, '__call__')
+ for mark, (kind, data, pos) in stream:
+ if mark is ENTER:
+ if callable_value:
+ value = self.value(self.name, (kind, data, pos))
+ else:
+ value = self.value
+ if value is None:
+ attrs = data[1] - [QName(self.name)]
+ else:
+ attrs = data[1] | [(QName(self.name), value)]
+ data = (data[0], attrs)
+ yield mark, (kind, data, pos)
+
+
+
+class StreamBuffer(Stream):
+ """Stream event buffer used for cut and copy transformations."""
+
+ def __init__(self):
+ """Create the buffer."""
+ Stream.__init__(self, [])
+
+ def append(self, event):
+ """Add an event to the buffer.
+
+ :param event: the markup event to add
+ """
+ self.events.append(event)
+
+ def reset(self):
+ """Empty the buffer of events."""
+ del self.events[:]
+
+
+class CopyTransformation(object):
+ """Copy selected events into a buffer for later insertion."""
+
+ def __init__(self, buffer, accumulate=False):
+ """Create the copy transformation.
+
+ :param buffer: the `StreamBuffer` in which the selection should be
+ stored
+ """
+ if not accumulate:
+ buffer.reset()
+ self.buffer = buffer
+ self.accumulate = accumulate
+
+ def __call__(self, stream):
+ """Apply the transformation to the marked stream.
+
+ :param stream: the marked event stream to filter
+ """
+ stream = PushBackStream(stream)
+
+ for mark, event in stream:
+ if mark:
+ if not self.accumulate:
+ self.buffer.reset()
+ events = [(mark, event)]
+ self.buffer.append(event)
+ start = mark
+ for mark, event in stream:
+ if start is not ENTER and mark != start:
+ stream.push((mark, event))
+ break
+ events.append((mark, event))
+ self.buffer.append(event)
+ if start is ENTER and mark is EXIT:
+ break
+ for i in events:
+ yield i
+ else:
+ yield mark, event
+
+
+class CutTransformation(object):
+ """Cut selected events into a buffer for later insertion and remove the
+ selection.
+ """
+
+ def __init__(self, buffer, accumulate=False):
+ """Create the cut transformation.
+
+ :param buffer: the `StreamBuffer` in which the selection should be
+ stored
+ """
+ self.buffer = buffer
+ self.accumulate = accumulate
+
+
+ def __call__(self, stream):
+ """Apply the transform filter to the marked stream.
+
+ :param stream: the marked event stream to filter
+ """
+ attributes = []
+ stream = PushBackStream(stream)
+ broken = False
+ if not self.accumulate:
+ self.buffer.reset()
+ for mark, event in stream:
+ if mark:
+ # Send a BREAK event if there was no other event sent between
+ if not self.accumulate:
+ if not broken and self.buffer:
+ yield BREAK, (BREAK, None, None)
+ self.buffer.reset()
+ self.buffer.append(event)
+ start = mark
+ if mark is ATTR:
+ attributes.extend([name for name, _ in event[1][1]])
+ for mark, event in stream:
+ if start is mark is ATTR:
+ attributes.extend([name for name, _ in event[1][1]])
+ # Handle non-element contiguous selection
+ if start is not ENTER and mark != start:
+ # Operating on the attributes of a START event
+ if start is ATTR:
+ kind, data, pos = event
+ assert kind is START
+ data = (data[0], data[1] - attributes)
+ attributes = None
+ stream.push((mark, (kind, data, pos)))
+ else:
+ stream.push((mark, event))
+ break
+ self.buffer.append(event)
+ if start is ENTER and mark is EXIT:
+ break
+ broken = False
+ else:
+ broken = True
+ yield mark, event
+ if not broken and self.buffer:
+ yield BREAK, (BREAK, None, None)
diff --git a/websdk/genshi/input.py b/websdk/genshi/input.py
new file mode 100644
index 0000000..039e5e5
--- /dev/null
+++ b/websdk/genshi/input.py
@@ -0,0 +1,443 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Support for constructing markup streams from files, strings, or other
+sources.
+"""
+
+from itertools import chain
+import htmlentitydefs as entities
+import HTMLParser as html
+from StringIO import StringIO
+from xml.parsers import expat
+
+from genshi.core import Attrs, QName, Stream, stripentities
+from genshi.core import START, END, XML_DECL, DOCTYPE, TEXT, START_NS, \
+ END_NS, START_CDATA, END_CDATA, PI, COMMENT
+
+__all__ = ['ET', 'ParseError', 'XMLParser', 'XML', 'HTMLParser', 'HTML']
+__docformat__ = 'restructuredtext en'
+
+
+def ET(element):
+ """Convert a given ElementTree element to a markup stream.
+
+ :param element: an ElementTree element
+ :return: a markup stream
+ """
+ tag_name = QName(element.tag.lstrip('{'))
+ attrs = Attrs([(QName(attr.lstrip('{')), value)
+ for attr, value in element.items()])
+
+ yield START, (tag_name, attrs), (None, -1, -1)
+ if element.text:
+ yield TEXT, element.text, (None, -1, -1)
+ for child in element.getchildren():
+ for item in ET(child):
+ yield item
+ yield END, tag_name, (None, -1, -1)
+ if element.tail:
+ yield TEXT, element.tail, (None, -1, -1)
+
+
+class ParseError(Exception):
+ """Exception raised when fatal syntax errors are found in the input being
+ parsed.
+ """
+
+ def __init__(self, message, filename=None, lineno=-1, offset=-1):
+ """Exception initializer.
+
+ :param message: the error message from the parser
+ :param filename: the path to the file that was parsed
+ :param lineno: the number of the line on which the error was encountered
+ :param offset: the column number where the error was encountered
+ """
+ self.msg = message
+ if filename:
+ message += ', in ' + filename
+ Exception.__init__(self, message)
+ self.filename = filename or '<string>'
+ self.lineno = lineno
+ self.offset = offset
+
+
+class XMLParser(object):
+ """Generator-based XML parser based on roughly equivalent code in
+ Kid/ElementTree.
+
+ The parsing is initiated by iterating over the parser object:
+
+ >>> parser = XMLParser(StringIO('<root id="2"><child>Foo</child></root>'))
+ >>> for kind, data, pos in parser:
+ ... print('%s %s' % (kind, data))
+ START (QName('root'), Attrs([(QName('id'), u'2')]))
+ START (QName('child'), Attrs())
+ TEXT Foo
+ END child
+ END root
+ """
+
+ _entitydefs = ['<!ENTITY %s "&#%d;">' % (name, value) for name, value in
+ entities.name2codepoint.items()]
+ _external_dtd = '\n'.join(_entitydefs)
+
+ def __init__(self, source, filename=None, encoding=None):
+ """Initialize the parser for the given XML input.
+
+ :param source: the XML text as a file-like object
+ :param filename: the name of the file, if appropriate
+ :param encoding: the encoding of the file; if not specified, the
+ encoding is assumed to be ASCII, UTF-8, or UTF-16, or
+ whatever the encoding specified in the XML declaration
+ (if any)
+ """
+ self.source = source
+ self.filename = filename
+
+ # Setup the Expat parser
+ parser = expat.ParserCreate(encoding, '}')
+ parser.buffer_text = True
+ parser.returns_unicode = True
+ parser.ordered_attributes = True
+
+ parser.StartElementHandler = self._handle_start
+ parser.EndElementHandler = self._handle_end
+ parser.CharacterDataHandler = self._handle_data
+ parser.StartDoctypeDeclHandler = self._handle_doctype
+ parser.StartNamespaceDeclHandler = self._handle_start_ns
+ parser.EndNamespaceDeclHandler = self._handle_end_ns
+ parser.StartCdataSectionHandler = self._handle_start_cdata
+ parser.EndCdataSectionHandler = self._handle_end_cdata
+ parser.ProcessingInstructionHandler = self._handle_pi
+ parser.XmlDeclHandler = self._handle_xml_decl
+ parser.CommentHandler = self._handle_comment
+
+ # Tell Expat that we'll handle non-XML entities ourselves
+ # (in _handle_other)
+ parser.DefaultHandler = self._handle_other
+ parser.SetParamEntityParsing(expat.XML_PARAM_ENTITY_PARSING_ALWAYS)
+ parser.UseForeignDTD()
+ parser.ExternalEntityRefHandler = self._build_foreign
+
+ self.expat = parser
+ self._queue = []
+
+ def parse(self):
+ """Generator that parses the XML source, yielding markup events.
+
+ :return: a markup event stream
+ :raises ParseError: if the XML text is not well formed
+ """
+ def _generate():
+ try:
+ bufsize = 4 * 1024 # 4K
+ done = False
+ while 1:
+ while not done and len(self._queue) == 0:
+ data = self.source.read(bufsize)
+ if data == '': # end of data
+ if hasattr(self, 'expat'):
+ self.expat.Parse('', True)
+ del self.expat # get rid of circular references
+ done = True
+ else:
+ if isinstance(data, unicode):
+ data = data.encode('utf-8')
+ self.expat.Parse(data, False)
+ for event in self._queue:
+ yield event
+ self._queue = []
+ if done:
+ break
+ except expat.ExpatError, e:
+ msg = str(e)
+ raise ParseError(msg, self.filename, e.lineno, e.offset)
+ return Stream(_generate()).filter(_coalesce)
+
+ def __iter__(self):
+ return iter(self.parse())
+
+ def _build_foreign(self, context, base, sysid, pubid):
+ parser = self.expat.ExternalEntityParserCreate(context)
+ parser.ParseFile(StringIO(self._external_dtd))
+ return 1
+
+ def _enqueue(self, kind, data=None, pos=None):
+ if pos is None:
+ pos = self._getpos()
+ if kind is TEXT:
+ # Expat reports the *end* of the text event as current position. We
+ # try to fix that up here as much as possible. Unfortunately, the
+ # offset is only valid for single-line text. For multi-line text,
+ # it is apparently not possible to determine at what offset it
+ # started
+ if '\n' in data:
+ lines = data.splitlines()
+ lineno = pos[1] - len(lines) + 1
+ offset = -1
+ else:
+ lineno = pos[1]
+ offset = pos[2] - len(data)
+ pos = (pos[0], lineno, offset)
+ self._queue.append((kind, data, pos))
+
+ def _getpos_unknown(self):
+ return (self.filename, -1, -1)
+
+ def _getpos(self):
+ return (self.filename, self.expat.CurrentLineNumber,
+ self.expat.CurrentColumnNumber)
+
+ def _handle_start(self, tag, attrib):
+ attrs = Attrs([(QName(name), value) for name, value in
+ zip(*[iter(attrib)] * 2)])
+ self._enqueue(START, (QName(tag), attrs))
+
+ def _handle_end(self, tag):
+ self._enqueue(END, QName(tag))
+
+ def _handle_data(self, text):
+ self._enqueue(TEXT, text)
+
+ def _handle_xml_decl(self, version, encoding, standalone):
+ self._enqueue(XML_DECL, (version, encoding, standalone))
+
+ def _handle_doctype(self, name, sysid, pubid, has_internal_subset):
+ self._enqueue(DOCTYPE, (name, pubid, sysid))
+
+ def _handle_start_ns(self, prefix, uri):
+ self._enqueue(START_NS, (prefix or '', uri))
+
+ def _handle_end_ns(self, prefix):
+ self._enqueue(END_NS, prefix or '')
+
+ def _handle_start_cdata(self):
+ self._enqueue(START_CDATA)
+
+ def _handle_end_cdata(self):
+ self._enqueue(END_CDATA)
+
+ def _handle_pi(self, target, data):
+ self._enqueue(PI, (target, data))
+
+ def _handle_comment(self, text):
+ self._enqueue(COMMENT, text)
+
+ def _handle_other(self, text):
+ if text.startswith('&'):
+ # deal with undefined entities
+ try:
+ text = unichr(entities.name2codepoint[text[1:-1]])
+ self._enqueue(TEXT, text)
+ except KeyError:
+ filename, lineno, offset = self._getpos()
+ error = expat.error('undefined entity "%s": line %d, column %d'
+ % (text, lineno, offset))
+ error.code = expat.errors.XML_ERROR_UNDEFINED_ENTITY
+ error.lineno = lineno
+ error.offset = offset
+ raise error
+
+
+def XML(text):
+ """Parse the given XML source and return a markup stream.
+
+ Unlike with `XMLParser`, the returned stream is reusable, meaning it can be
+ iterated over multiple times:
+
+ >>> xml = XML('<doc><elem>Foo</elem><elem>Bar</elem></doc>')
+ >>> print(xml)
+ <doc><elem>Foo</elem><elem>Bar</elem></doc>
+ >>> print(xml.select('elem'))
+ <elem>Foo</elem><elem>Bar</elem>
+ >>> print(xml.select('elem/text()'))
+ FooBar
+
+ :param text: the XML source
+ :return: the parsed XML event stream
+ :raises ParseError: if the XML text is not well-formed
+ """
+ return Stream(list(XMLParser(StringIO(text))))
+
+
+class HTMLParser(html.HTMLParser, object):
+ """Parser for HTML input based on the Python `HTMLParser` module.
+
+ This class provides the same interface for generating stream events as
+ `XMLParser`, and attempts to automatically balance tags.
+
+ The parsing is initiated by iterating over the parser object:
+
+ >>> parser = HTMLParser(StringIO('<UL compact><LI>Foo</UL>'))
+ >>> for kind, data, pos in parser:
+ ... print('%s %s' % (kind, data))
+ START (QName('ul'), Attrs([(QName('compact'), u'compact')]))
+ START (QName('li'), Attrs())
+ TEXT Foo
+ END li
+ END ul
+ """
+
+ _EMPTY_ELEMS = frozenset(['area', 'base', 'basefont', 'br', 'col', 'frame',
+ 'hr', 'img', 'input', 'isindex', 'link', 'meta',
+ 'param'])
+
+ def __init__(self, source, filename=None, encoding='utf-8'):
+ """Initialize the parser for the given HTML input.
+
+ :param source: the HTML text as a file-like object
+ :param filename: the name of the file, if known
+ :param filename: encoding of the file; ignored if the input is unicode
+ """
+ html.HTMLParser.__init__(self)
+ self.source = source
+ self.filename = filename
+ self.encoding = encoding
+ self._queue = []
+ self._open_tags = []
+
+ def parse(self):
+ """Generator that parses the HTML source, yielding markup events.
+
+ :return: a markup event stream
+ :raises ParseError: if the HTML text is not well formed
+ """
+ def _generate():
+ try:
+ bufsize = 4 * 1024 # 4K
+ done = False
+ while 1:
+ while not done and len(self._queue) == 0:
+ data = self.source.read(bufsize)
+ if data == '': # end of data
+ self.close()
+ done = True
+ else:
+ self.feed(data)
+ for kind, data, pos in self._queue:
+ yield kind, data, pos
+ self._queue = []
+ if done:
+ open_tags = self._open_tags
+ open_tags.reverse()
+ for tag in open_tags:
+ yield END, QName(tag), pos
+ break
+ except html.HTMLParseError, e:
+ msg = '%s: line %d, column %d' % (e.msg, e.lineno, e.offset)
+ raise ParseError(msg, self.filename, e.lineno, e.offset)
+ return Stream(_generate()).filter(_coalesce)
+
+ def __iter__(self):
+ return iter(self.parse())
+
+ def _enqueue(self, kind, data, pos=None):
+ if pos is None:
+ pos = self._getpos()
+ self._queue.append((kind, data, pos))
+
+ def _getpos(self):
+ lineno, column = self.getpos()
+ return (self.filename, lineno, column)
+
+ def handle_starttag(self, tag, attrib):
+ fixed_attrib = []
+ for name, value in attrib: # Fixup minimized attributes
+ if value is None:
+ value = unicode(name)
+ elif not isinstance(value, unicode):
+ value = value.decode(self.encoding, 'replace')
+ fixed_attrib.append((QName(name), stripentities(value)))
+
+ self._enqueue(START, (QName(tag), Attrs(fixed_attrib)))
+ if tag in self._EMPTY_ELEMS:
+ self._enqueue(END, QName(tag))
+ else:
+ self._open_tags.append(tag)
+
+ def handle_endtag(self, tag):
+ if tag not in self._EMPTY_ELEMS:
+ while self._open_tags:
+ open_tag = self._open_tags.pop()
+ self._enqueue(END, QName(open_tag))
+ if open_tag.lower() == tag.lower():
+ break
+
+ def handle_data(self, text):
+ if not isinstance(text, unicode):
+ text = text.decode(self.encoding, 'replace')
+ self._enqueue(TEXT, text)
+
+ def handle_charref(self, name):
+ if name.lower().startswith('x'):
+ text = unichr(int(name[1:], 16))
+ else:
+ text = unichr(int(name))
+ self._enqueue(TEXT, text)
+
+ def handle_entityref(self, name):
+ try:
+ text = unichr(entities.name2codepoint[name])
+ except KeyError:
+ text = '&%s;' % name
+ self._enqueue(TEXT, text)
+
+ def handle_pi(self, data):
+ target, data = data.split(None, 1)
+ if data.endswith('?'):
+ data = data[:-1]
+ self._enqueue(PI, (target.strip(), data.strip()))
+
+ def handle_comment(self, text):
+ self._enqueue(COMMENT, text)
+
+
+def HTML(text, encoding='utf-8'):
+ """Parse the given HTML source and return a markup stream.
+
+ Unlike with `HTMLParser`, the returned stream is reusable, meaning it can be
+ iterated over multiple times:
+
+ >>> html = HTML('<body><h1>Foo</h1></body>')
+ >>> print(html)
+ <body><h1>Foo</h1></body>
+ >>> print(html.select('h1'))
+ <h1>Foo</h1>
+ >>> print(html.select('h1/text()'))
+ Foo
+
+ :param text: the HTML source
+ :return: the parsed XML event stream
+ :raises ParseError: if the HTML text is not well-formed, and error recovery
+ fails
+ """
+ return Stream(list(HTMLParser(StringIO(text), encoding=encoding)))
+
+
+def _coalesce(stream):
+ """Coalesces adjacent TEXT events into a single event."""
+ textbuf = []
+ textpos = None
+ for kind, data, pos in chain(stream, [(None, None, None)]):
+ if kind is TEXT:
+ textbuf.append(data)
+ if textpos is None:
+ textpos = pos
+ else:
+ if textbuf:
+ yield TEXT, ''.join(textbuf), textpos
+ del textbuf[:]
+ textpos = None
+ if kind:
+ yield kind, data, pos
diff --git a/websdk/genshi/output.py b/websdk/genshi/output.py
new file mode 100644
index 0000000..2ebb38b
--- /dev/null
+++ b/websdk/genshi/output.py
@@ -0,0 +1,838 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""This module provides different kinds of serialization methods for XML event
+streams.
+"""
+
+from itertools import chain
+import re
+
+from genshi.core import escape, Attrs, Markup, Namespace, QName, StreamEventKind
+from genshi.core import START, END, TEXT, XML_DECL, DOCTYPE, START_NS, END_NS, \
+ START_CDATA, END_CDATA, PI, COMMENT, XML_NAMESPACE
+
+__all__ = ['encode', 'get_serializer', 'DocType', 'XMLSerializer',
+ 'XHTMLSerializer', 'HTMLSerializer', 'TextSerializer']
+__docformat__ = 'restructuredtext en'
+
+
+def encode(iterator, method='xml', encoding='utf-8', out=None):
+ """Encode serializer output into a string.
+
+ :param iterator: the iterator returned from serializing a stream (basically
+ any iterator that yields unicode objects)
+ :param method: the serialization method; determines how characters not
+ representable in the specified encoding are treated
+ :param encoding: how the output string should be encoded; if set to `None`,
+ this method returns a `unicode` object
+ :param out: a file-like object that the output should be written to
+ instead of being returned as one big string; note that if
+ this is a file or socket (or similar), the `encoding` must
+ not be `None` (that is, the output must be encoded)
+ :return: a `str` or `unicode` object (depending on the `encoding`
+ parameter), or `None` if the `out` parameter is provided
+
+ :since: version 0.4.1
+ :note: Changed in 0.5: added the `out` parameter
+ """
+ if encoding is not None:
+ errors = 'replace'
+ if method != 'text' and not isinstance(method, TextSerializer):
+ errors = 'xmlcharrefreplace'
+ _encode = lambda string: string.encode(encoding, errors)
+ else:
+ _encode = lambda string: string
+ if out is None:
+ return _encode(''.join(list(iterator)))
+ for chunk in iterator:
+ out.write(_encode(chunk))
+
+
+def get_serializer(method='xml', **kwargs):
+ """Return a serializer object for the given method.
+
+ :param method: the serialization method; can be either "xml", "xhtml",
+ "html", "text", or a custom serializer class
+
+ Any additional keyword arguments are passed to the serializer, and thus
+ depend on the `method` parameter value.
+
+ :see: `XMLSerializer`, `XHTMLSerializer`, `HTMLSerializer`, `TextSerializer`
+ :since: version 0.4.1
+ """
+ if isinstance(method, basestring):
+ method = {'xml': XMLSerializer,
+ 'xhtml': XHTMLSerializer,
+ 'html': HTMLSerializer,
+ 'text': TextSerializer}[method.lower()]
+ return method(**kwargs)
+
+
+class DocType(object):
+ """Defines a number of commonly used DOCTYPE declarations as constants."""
+
+ HTML_STRICT = (
+ 'html', '-//W3C//DTD HTML 4.01//EN',
+ 'http://www.w3.org/TR/html4/strict.dtd'
+ )
+ HTML_TRANSITIONAL = (
+ 'html', '-//W3C//DTD HTML 4.01 Transitional//EN',
+ 'http://www.w3.org/TR/html4/loose.dtd'
+ )
+ HTML_FRAMESET = (
+ 'html', '-//W3C//DTD HTML 4.01 Frameset//EN',
+ 'http://www.w3.org/TR/html4/frameset.dtd'
+ )
+ HTML = HTML_STRICT
+
+ HTML5 = ('html', None, None)
+
+ XHTML_STRICT = (
+ 'html', '-//W3C//DTD XHTML 1.0 Strict//EN',
+ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
+ )
+ XHTML_TRANSITIONAL = (
+ 'html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
+ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'
+ )
+ XHTML_FRAMESET = (
+ 'html', '-//W3C//DTD XHTML 1.0 Frameset//EN',
+ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd'
+ )
+ XHTML = XHTML_STRICT
+
+ XHTML11 = (
+ 'html', '-//W3C//DTD XHTML 1.1//EN',
+ 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
+ )
+
+ SVG_FULL = (
+ 'svg', '-//W3C//DTD SVG 1.1//EN',
+ 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'
+ )
+ SVG_BASIC = (
+ 'svg', '-//W3C//DTD SVG Basic 1.1//EN',
+ 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd'
+ )
+ SVG_TINY = (
+ 'svg', '-//W3C//DTD SVG Tiny 1.1//EN',
+ 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd'
+ )
+ SVG = SVG_FULL
+
+ @classmethod
+ def get(cls, name):
+ """Return the ``(name, pubid, sysid)`` tuple of the ``DOCTYPE``
+ declaration for the specified name.
+
+ The following names are recognized in this version:
+ * "html" or "html-strict" for the HTML 4.01 strict DTD
+ * "html-transitional" for the HTML 4.01 transitional DTD
+ * "html-frameset" for the HTML 4.01 frameset DTD
+ * "html5" for the ``DOCTYPE`` proposed for HTML5
+ * "xhtml" or "xhtml-strict" for the XHTML 1.0 strict DTD
+ * "xhtml-transitional" for the XHTML 1.0 transitional DTD
+ * "xhtml-frameset" for the XHTML 1.0 frameset DTD
+ * "xhtml11" for the XHTML 1.1 DTD
+ * "svg" or "svg-full" for the SVG 1.1 DTD
+ * "svg-basic" for the SVG Basic 1.1 DTD
+ * "svg-tiny" for the SVG Tiny 1.1 DTD
+
+ :param name: the name of the ``DOCTYPE``
+ :return: the ``(name, pubid, sysid)`` tuple for the requested
+ ``DOCTYPE``, or ``None`` if the name is not recognized
+ :since: version 0.4.1
+ """
+ return {
+ 'html': cls.HTML, 'html-strict': cls.HTML_STRICT,
+ 'html-transitional': DocType.HTML_TRANSITIONAL,
+ 'html-frameset': DocType.HTML_FRAMESET,
+ 'html5': cls.HTML5,
+ 'xhtml': cls.XHTML, 'xhtml-strict': cls.XHTML_STRICT,
+ 'xhtml-transitional': cls.XHTML_TRANSITIONAL,
+ 'xhtml-frameset': cls.XHTML_FRAMESET,
+ 'xhtml11': cls.XHTML11,
+ 'svg': cls.SVG, 'svg-full': cls.SVG_FULL,
+ 'svg-basic': cls.SVG_BASIC,
+ 'svg-tiny': cls.SVG_TINY
+ }.get(name.lower())
+
+
+class XMLSerializer(object):
+ """Produces XML text from an event stream.
+
+ >>> from genshi.builder import tag
+ >>> elem = tag.div(tag.a(href='foo'), tag.br, tag.hr(noshade=True))
+ >>> print(''.join(XMLSerializer()(elem.generate())))
+ <div><a href="foo"/><br/><hr noshade="True"/></div>
+ """
+
+ _PRESERVE_SPACE = frozenset()
+
+ def __init__(self, doctype=None, strip_whitespace=True,
+ namespace_prefixes=None, cache=True):
+ """Initialize the XML serializer.
+
+ :param doctype: a ``(name, pubid, sysid)`` tuple that represents the
+ DOCTYPE declaration that should be included at the top
+ of the generated output, or the name of a DOCTYPE as
+ defined in `DocType.get`
+ :param strip_whitespace: whether extraneous whitespace should be
+ stripped from the output
+ :param cache: whether to cache the text output per event, which
+ improves performance for repetitive markup
+ :note: Changed in 0.4.2: The `doctype` parameter can now be a string.
+ :note: Changed in 0.6: The `cache` parameter was added
+ """
+ self.filters = [EmptyTagFilter()]
+ if strip_whitespace:
+ self.filters.append(WhitespaceFilter(self._PRESERVE_SPACE))
+ self.filters.append(NamespaceFlattener(prefixes=namespace_prefixes,
+ cache=cache))
+ if doctype:
+ self.filters.append(DocTypeInserter(doctype))
+ self.cache = cache
+
+ def __call__(self, stream):
+ have_decl = have_doctype = False
+ in_cdata = False
+
+ cache = {}
+ cache_get = cache.get
+ if self.cache:
+ def _emit(kind, input, output):
+ cache[kind, input] = output
+ return output
+ else:
+ def _emit(kind, input, output):
+ return output
+
+ for filter_ in self.filters:
+ stream = filter_(stream)
+ for kind, data, pos in stream:
+ cached = cache_get((kind, data))
+ if cached is not None:
+ yield cached
+
+ elif kind is START or kind is EMPTY:
+ tag, attrib = data
+ buf = ['<', tag]
+ for attr, value in attrib:
+ buf += [' ', attr, '="', escape(value), '"']
+ buf.append(kind is EMPTY and '/>' or '>')
+ yield _emit(kind, data, Markup(''.join(buf)))
+
+ elif kind is END:
+ yield _emit(kind, data, Markup('</%s>' % data))
+
+ elif kind is TEXT:
+ if in_cdata:
+ yield _emit(kind, data, data)
+ else:
+ yield _emit(kind, data, escape(data, quotes=False))
+
+ elif kind is COMMENT:
+ yield _emit(kind, data, Markup('<!--%s-->' % data))
+
+ elif kind is XML_DECL and not have_decl:
+ version, encoding, standalone = data
+ buf = ['<?xml version="%s"' % version]
+ if encoding:
+ buf.append(' encoding="%s"' % encoding)
+ if standalone != -1:
+ standalone = standalone and 'yes' or 'no'
+ buf.append(' standalone="%s"' % standalone)
+ buf.append('?>\n')
+ yield Markup(''.join(buf))
+ have_decl = True
+
+ elif kind is DOCTYPE and not have_doctype:
+ name, pubid, sysid = data
+ buf = ['<!DOCTYPE %s']
+ if pubid:
+ buf.append(' PUBLIC "%s"')
+ elif sysid:
+ buf.append(' SYSTEM')
+ if sysid:
+ buf.append(' "%s"')
+ buf.append('>\n')
+ yield Markup(''.join(buf)) % tuple([p for p in data if p])
+ have_doctype = True
+
+ elif kind is START_CDATA:
+ yield Markup('<![CDATA[')
+ in_cdata = True
+
+ elif kind is END_CDATA:
+ yield Markup(']]>')
+ in_cdata = False
+
+ elif kind is PI:
+ yield _emit(kind, data, Markup('<?%s %s?>' % data))
+
+
+class XHTMLSerializer(XMLSerializer):
+ """Produces XHTML text from an event stream.
+
+ >>> from genshi.builder import tag
+ >>> elem = tag.div(tag.a(href='foo'), tag.br, tag.hr(noshade=True))
+ >>> print(''.join(XHTMLSerializer()(elem.generate())))
+ <div><a href="foo"></a><br /><hr noshade="noshade" /></div>
+ """
+
+ _EMPTY_ELEMS = frozenset(['area', 'base', 'basefont', 'br', 'col', 'frame',
+ 'hr', 'img', 'input', 'isindex', 'link', 'meta',
+ 'param'])
+ _BOOLEAN_ATTRS = frozenset(['selected', 'checked', 'compact', 'declare',
+ 'defer', 'disabled', 'ismap', 'multiple',
+ 'nohref', 'noresize', 'noshade', 'nowrap'])
+ _PRESERVE_SPACE = frozenset([
+ QName('pre'), QName('http://www.w3.org/1999/xhtml}pre'),
+ QName('textarea'), QName('http://www.w3.org/1999/xhtml}textarea')
+ ])
+
+ def __init__(self, doctype=None, strip_whitespace=True,
+ namespace_prefixes=None, drop_xml_decl=True, cache=True):
+ super(XHTMLSerializer, self).__init__(doctype, False)
+ self.filters = [EmptyTagFilter()]
+ if strip_whitespace:
+ self.filters.append(WhitespaceFilter(self._PRESERVE_SPACE))
+ namespace_prefixes = namespace_prefixes or {}
+ namespace_prefixes['http://www.w3.org/1999/xhtml'] = ''
+ self.filters.append(NamespaceFlattener(prefixes=namespace_prefixes,
+ cache=cache))
+ if doctype:
+ self.filters.append(DocTypeInserter(doctype))
+ self.drop_xml_decl = drop_xml_decl
+ self.cache = cache
+
+ def __call__(self, stream):
+ boolean_attrs = self._BOOLEAN_ATTRS
+ empty_elems = self._EMPTY_ELEMS
+ drop_xml_decl = self.drop_xml_decl
+ have_decl = have_doctype = False
+ in_cdata = False
+
+ cache = {}
+ cache_get = cache.get
+ if self.cache:
+ def _emit(kind, input, output):
+ cache[kind, input] = output
+ return output
+ else:
+ def _emit(kind, input, output):
+ return output
+
+ for filter_ in self.filters:
+ stream = filter_(stream)
+ for kind, data, pos in stream:
+ cached = cache_get((kind, data))
+ if cached is not None:
+ yield cached
+
+ elif kind is START or kind is EMPTY:
+ tag, attrib = data
+ buf = ['<', tag]
+ for attr, value in attrib:
+ if attr in boolean_attrs:
+ value = attr
+ elif attr == 'xml:lang' and 'lang' not in attrib:
+ buf += [' lang="', escape(value), '"']
+ elif attr == 'xml:space':
+ continue
+ buf += [' ', attr, '="', escape(value), '"']
+ if kind is EMPTY:
+ if tag in empty_elems:
+ buf.append(' />')
+ else:
+ buf.append('></%s>' % tag)
+ else:
+ buf.append('>')
+ yield _emit(kind, data, Markup(''.join(buf)))
+
+ elif kind is END:
+ yield _emit(kind, data, Markup('</%s>' % data))
+
+ elif kind is TEXT:
+ if in_cdata:
+ yield _emit(kind, data, data)
+ else:
+ yield _emit(kind, data, escape(data, quotes=False))
+
+ elif kind is COMMENT:
+ yield _emit(kind, data, Markup('<!--%s-->' % data))
+
+ elif kind is DOCTYPE and not have_doctype:
+ name, pubid, sysid = data
+ buf = ['<!DOCTYPE %s']
+ if pubid:
+ buf.append(' PUBLIC "%s"')
+ elif sysid:
+ buf.append(' SYSTEM')
+ if sysid:
+ buf.append(' "%s"')
+ buf.append('>\n')
+ yield Markup(''.join(buf)) % tuple([p for p in data if p])
+ have_doctype = True
+
+ elif kind is XML_DECL and not have_decl and not drop_xml_decl:
+ version, encoding, standalone = data
+ buf = ['<?xml version="%s"' % version]
+ if encoding:
+ buf.append(' encoding="%s"' % encoding)
+ if standalone != -1:
+ standalone = standalone and 'yes' or 'no'
+ buf.append(' standalone="%s"' % standalone)
+ buf.append('?>\n')
+ yield Markup(''.join(buf))
+ have_decl = True
+
+ elif kind is START_CDATA:
+ yield Markup('<![CDATA[')
+ in_cdata = True
+
+ elif kind is END_CDATA:
+ yield Markup(']]>')
+ in_cdata = False
+
+ elif kind is PI:
+ yield _emit(kind, data, Markup('<?%s %s?>' % data))
+
+
+class HTMLSerializer(XHTMLSerializer):
+ """Produces HTML text from an event stream.
+
+ >>> from genshi.builder import tag
+ >>> elem = tag.div(tag.a(href='foo'), tag.br, tag.hr(noshade=True))
+ >>> print(''.join(HTMLSerializer()(elem.generate())))
+ <div><a href="foo"></a><br><hr noshade></div>
+ """
+
+ _NOESCAPE_ELEMS = frozenset([
+ QName('script'), QName('http://www.w3.org/1999/xhtml}script'),
+ QName('style'), QName('http://www.w3.org/1999/xhtml}style')
+ ])
+
+ def __init__(self, doctype=None, strip_whitespace=True, cache=True):
+ """Initialize the HTML serializer.
+
+ :param doctype: a ``(name, pubid, sysid)`` tuple that represents the
+ DOCTYPE declaration that should be included at the top
+ of the generated output
+ :param strip_whitespace: whether extraneous whitespace should be
+ stripped from the output
+ :param cache: whether to cache the text output per event, which
+ improves performance for repetitive markup
+ :note: Changed in 0.6: The `cache` parameter was added
+ """
+ super(HTMLSerializer, self).__init__(doctype, False)
+ self.filters = [EmptyTagFilter()]
+ if strip_whitespace:
+ self.filters.append(WhitespaceFilter(self._PRESERVE_SPACE,
+ self._NOESCAPE_ELEMS))
+ self.filters.append(NamespaceFlattener(prefixes={
+ 'http://www.w3.org/1999/xhtml': ''
+ }, cache=cache))
+ if doctype:
+ self.filters.append(DocTypeInserter(doctype))
+ self.cache = True
+
+ def __call__(self, stream):
+ boolean_attrs = self._BOOLEAN_ATTRS
+ empty_elems = self._EMPTY_ELEMS
+ noescape_elems = self._NOESCAPE_ELEMS
+ have_doctype = False
+ noescape = False
+
+ cache = {}
+ cache_get = cache.get
+ if self.cache:
+ def _emit(kind, input, output):
+ cache[kind, input] = output
+ return output
+ else:
+ def _emit(kind, input, output):
+ return output
+
+ for filter_ in self.filters:
+ stream = filter_(stream)
+ for kind, data, _ in stream:
+ output = cache_get((kind, data))
+ if output is not None:
+ yield output
+ if (kind is START or kind is EMPTY) \
+ and data[0] in noescape_elems:
+ noescape = True
+ elif kind is END:
+ noescape = False
+
+ elif kind is START or kind is EMPTY:
+ tag, attrib = data
+ buf = ['<', tag]
+ for attr, value in attrib:
+ if attr in boolean_attrs:
+ if value:
+ buf += [' ', attr]
+ elif ':' in attr:
+ if attr == 'xml:lang' and 'lang' not in attrib:
+ buf += [' lang="', escape(value), '"']
+ elif attr != 'xmlns':
+ buf += [' ', attr, '="', escape(value), '"']
+ buf.append('>')
+ if kind is EMPTY:
+ if tag not in empty_elems:
+ buf.append('</%s>' % tag)
+ yield _emit(kind, data, Markup(''.join(buf)))
+ if tag in noescape_elems:
+ noescape = True
+
+ elif kind is END:
+ yield _emit(kind, data, Markup('</%s>' % data))
+ noescape = False
+
+ elif kind is TEXT:
+ if noescape:
+ yield _emit(kind, data, data)
+ else:
+ yield _emit(kind, data, escape(data, quotes=False))
+
+ elif kind is COMMENT:
+ yield _emit(kind, data, Markup('<!--%s-->' % data))
+
+ elif kind is DOCTYPE and not have_doctype:
+ name, pubid, sysid = data
+ buf = ['<!DOCTYPE %s']
+ if pubid:
+ buf.append(' PUBLIC "%s"')
+ elif sysid:
+ buf.append(' SYSTEM')
+ if sysid:
+ buf.append(' "%s"')
+ buf.append('>\n')
+ yield Markup(''.join(buf)) % tuple([p for p in data if p])
+ have_doctype = True
+
+ elif kind is PI:
+ yield _emit(kind, data, Markup('<?%s %s?>' % data))
+
+
+class TextSerializer(object):
+ """Produces plain text from an event stream.
+
+ Only text events are included in the output. Unlike the other serializer,
+ special XML characters are not escaped:
+
+ >>> from genshi.builder import tag
+ >>> elem = tag.div(tag.a('<Hello!>', href='foo'), tag.br)
+ >>> print(elem)
+ <div><a href="foo">&lt;Hello!&gt;</a><br/></div>
+ >>> print(''.join(TextSerializer()(elem.generate())))
+ <Hello!>
+
+ If text events contain literal markup (instances of the `Markup` class),
+ that markup is by default passed through unchanged:
+
+ >>> elem = tag.div(Markup('<a href="foo">Hello &amp; Bye!</a><br/>'))
+ >>> print(elem.generate().render(TextSerializer, encoding=None))
+ <a href="foo">Hello &amp; Bye!</a><br/>
+
+ You can use the ``strip_markup`` to change this behavior, so that tags and
+ entities are stripped from the output (or in the case of entities,
+ replaced with the equivalent character):
+
+ >>> print(elem.generate().render(TextSerializer, strip_markup=True,
+ ... encoding=None))
+ Hello & Bye!
+ """
+
+ def __init__(self, strip_markup=False):
+ """Create the serializer.
+
+ :param strip_markup: whether markup (tags and encoded characters) found
+ in the text should be removed
+ """
+ self.strip_markup = strip_markup
+
+ def __call__(self, stream):
+ strip_markup = self.strip_markup
+ for event in stream:
+ if event[0] is TEXT:
+ data = event[1]
+ if strip_markup and type(data) is Markup:
+ data = data.striptags().stripentities()
+ yield unicode(data)
+
+
+class EmptyTagFilter(object):
+ """Combines `START` and `STOP` events into `EMPTY` events for elements that
+ have no contents.
+ """
+
+ EMPTY = StreamEventKind('EMPTY')
+
+ def __call__(self, stream):
+ prev = (None, None, None)
+ for ev in stream:
+ if prev[0] is START:
+ if ev[0] is END:
+ prev = EMPTY, prev[1], prev[2]
+ yield prev
+ continue
+ else:
+ yield prev
+ if ev[0] is not START:
+ yield ev
+ prev = ev
+
+
+EMPTY = EmptyTagFilter.EMPTY
+
+
+class NamespaceFlattener(object):
+ r"""Output stream filter that removes namespace information from the stream,
+ instead adding namespace attributes and prefixes as needed.
+
+ :param prefixes: optional mapping of namespace URIs to prefixes
+
+ >>> from genshi.input import XML
+ >>> xml = XML('''<doc xmlns="NS1" xmlns:two="NS2">
+ ... <two:item/>
+ ... </doc>''')
+ >>> for kind, data, pos in NamespaceFlattener()(xml):
+ ... print('%s %r' % (kind, data))
+ START (u'doc', Attrs([('xmlns', u'NS1'), (u'xmlns:two', u'NS2')]))
+ TEXT u'\n '
+ START (u'two:item', Attrs())
+ END u'two:item'
+ TEXT u'\n'
+ END u'doc'
+ """
+
+ def __init__(self, prefixes=None, cache=True):
+ self.prefixes = {XML_NAMESPACE.uri: 'xml'}
+ if prefixes is not None:
+ self.prefixes.update(prefixes)
+ self.cache = cache
+
+ def __call__(self, stream):
+ cache = {}
+ cache_get = cache.get
+ if self.cache:
+ def _emit(kind, input, output, pos):
+ cache[kind, input] = output
+ return kind, output, pos
+ else:
+ def _emit(kind, input, output, pos):
+ return output
+
+ prefixes = dict([(v, [k]) for k, v in self.prefixes.items()])
+ namespaces = {XML_NAMESPACE.uri: ['xml']}
+ def _push_ns(prefix, uri):
+ namespaces.setdefault(uri, []).append(prefix)
+ prefixes.setdefault(prefix, []).append(uri)
+ cache.clear()
+ def _pop_ns(prefix):
+ uris = prefixes.get(prefix)
+ uri = uris.pop()
+ if not uris:
+ del prefixes[prefix]
+ if uri not in uris or uri != uris[-1]:
+ uri_prefixes = namespaces[uri]
+ uri_prefixes.pop()
+ if not uri_prefixes:
+ del namespaces[uri]
+ cache.clear()
+ return uri
+
+ ns_attrs = []
+ _push_ns_attr = ns_attrs.append
+ def _make_ns_attr(prefix, uri):
+ return 'xmlns%s' % (prefix and ':%s' % prefix or ''), uri
+
+ def _gen_prefix():
+ val = 0
+ while 1:
+ val += 1
+ yield 'ns%d' % val
+ _gen_prefix = _gen_prefix().next
+
+ for kind, data, pos in stream:
+ output = cache_get((kind, data))
+ if output is not None:
+ yield kind, output, pos
+
+ elif kind is START or kind is EMPTY:
+ tag, attrs = data
+
+ tagname = tag.localname
+ tagns = tag.namespace
+ if tagns:
+ if tagns in namespaces:
+ prefix = namespaces[tagns][-1]
+ if prefix:
+ tagname = '%s:%s' % (prefix, tagname)
+ else:
+ _push_ns_attr(('xmlns', tagns))
+ _push_ns('', tagns)
+
+ new_attrs = []
+ for attr, value in attrs:
+ attrname = attr.localname
+ attrns = attr.namespace
+ if attrns:
+ if attrns not in namespaces:
+ prefix = _gen_prefix()
+ _push_ns(prefix, attrns)
+ _push_ns_attr(('xmlns:%s' % prefix, attrns))
+ else:
+ prefix = namespaces[attrns][-1]
+ if prefix:
+ attrname = '%s:%s' % (prefix, attrname)
+ new_attrs.append((attrname, value))
+
+ yield _emit(kind, data, (tagname, Attrs(ns_attrs + new_attrs)), pos)
+ del ns_attrs[:]
+
+ elif kind is END:
+ tagname = data.localname
+ tagns = data.namespace
+ if tagns:
+ prefix = namespaces[tagns][-1]
+ if prefix:
+ tagname = '%s:%s' % (prefix, tagname)
+ yield _emit(kind, data, tagname, pos)
+
+ elif kind is START_NS:
+ prefix, uri = data
+ if uri not in namespaces:
+ prefix = prefixes.get(uri, [prefix])[-1]
+ _push_ns_attr(_make_ns_attr(prefix, uri))
+ _push_ns(prefix, uri)
+
+ elif kind is END_NS:
+ if data in prefixes:
+ uri = _pop_ns(data)
+ if ns_attrs:
+ attr = _make_ns_attr(data, uri)
+ if attr in ns_attrs:
+ ns_attrs.remove(attr)
+
+ else:
+ yield kind, data, pos
+
+
+class WhitespaceFilter(object):
+ """A filter that removes extraneous ignorable white space from the
+ stream.
+ """
+
+ def __init__(self, preserve=None, noescape=None):
+ """Initialize the filter.
+
+ :param preserve: a set or sequence of tag names for which white-space
+ should be preserved
+ :param noescape: a set or sequence of tag names for which text content
+ should not be escaped
+
+ The `noescape` set is expected to refer to elements that cannot contain
+ further child elements (such as ``<style>`` or ``<script>`` in HTML
+ documents).
+ """
+ if preserve is None:
+ preserve = []
+ self.preserve = frozenset(preserve)
+ if noescape is None:
+ noescape = []
+ self.noescape = frozenset(noescape)
+
+ def __call__(self, stream, ctxt=None, space=XML_NAMESPACE['space'],
+ trim_trailing_space=re.compile('[ \t]+(?=\n)').sub,
+ collapse_lines=re.compile('\n{2,}').sub):
+ mjoin = Markup('').join
+ preserve_elems = self.preserve
+ preserve = 0
+ noescape_elems = self.noescape
+ noescape = False
+
+ textbuf = []
+ push_text = textbuf.append
+ pop_text = textbuf.pop
+ for kind, data, pos in chain(stream, [(None, None, None)]):
+
+ if kind is TEXT:
+ if noescape:
+ data = Markup(data)
+ push_text(data)
+ else:
+ if textbuf:
+ if len(textbuf) > 1:
+ text = mjoin(textbuf, escape_quotes=False)
+ del textbuf[:]
+ else:
+ text = escape(pop_text(), quotes=False)
+ if not preserve:
+ text = collapse_lines('\n', trim_trailing_space('', text))
+ yield TEXT, Markup(text), pos
+
+ if kind is START:
+ tag, attrs = data
+ if preserve or (tag in preserve_elems or
+ attrs.get(space) == 'preserve'):
+ preserve += 1
+ if not noescape and tag in noescape_elems:
+ noescape = True
+
+ elif kind is END:
+ noescape = False
+ if preserve:
+ preserve -= 1
+
+ elif kind is START_CDATA:
+ noescape = True
+
+ elif kind is END_CDATA:
+ noescape = False
+
+ if kind:
+ yield kind, data, pos
+
+
+class DocTypeInserter(object):
+ """A filter that inserts the DOCTYPE declaration in the correct location,
+ after the XML declaration.
+ """
+ def __init__(self, doctype):
+ """Initialize the filter.
+
+ :param doctype: DOCTYPE as a string or DocType object.
+ """
+ if isinstance(doctype, basestring):
+ doctype = DocType.get(doctype)
+ self.doctype_event = (DOCTYPE, doctype, (None, -1, -1))
+
+ def __call__(self, stream):
+ doctype_inserted = False
+ for kind, data, pos in stream:
+ if not doctype_inserted:
+ doctype_inserted = True
+ if kind is XML_DECL:
+ yield (kind, data, pos)
+ yield self.doctype_event
+ continue
+ yield self.doctype_event
+
+ yield (kind, data, pos)
+
+ if not doctype_inserted:
+ yield self.doctype_event
diff --git a/websdk/genshi/path.py b/websdk/genshi/path.py
new file mode 100644
index 0000000..122fbf0
--- /dev/null
+++ b/websdk/genshi/path.py
@@ -0,0 +1,1528 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Basic support for evaluating XPath expressions against streams.
+
+>>> from genshi.input import XML
+>>> doc = XML('''<doc>
+... <items count="4">
+... <item status="new">
+... <summary>Foo</summary>
+... </item>
+... <item status="closed">
+... <summary>Bar</summary>
+... </item>
+... <item status="closed" resolution="invalid">
+... <summary>Baz</summary>
+... </item>
+... <item status="closed" resolution="fixed">
+... <summary>Waz</summary>
+... </item>
+... </items>
+... </doc>''')
+>>> print(doc.select('items/item[@status="closed" and '
+... '(@resolution="invalid" or not(@resolution))]/summary/text()'))
+BarBaz
+
+Because the XPath engine operates on markup streams (as opposed to tree
+structures), it only implements a subset of the full XPath 1.0 language.
+"""
+
+from collections import deque
+try:
+ reduce # builtin in Python < 3
+except NameError:
+ from functools import reduce
+from math import ceil, floor
+import operator
+import re
+from itertools import chain
+
+from genshi.core import Stream, Attrs, Namespace, QName
+from genshi.core import START, END, TEXT, START_NS, END_NS, COMMENT, PI, \
+ START_CDATA, END_CDATA
+
+__all__ = ['Path', 'PathSyntaxError']
+__docformat__ = 'restructuredtext en'
+
+
+class Axis(object):
+ """Defines constants for the various supported XPath axes."""
+
+ ATTRIBUTE = 'attribute'
+ CHILD = 'child'
+ DESCENDANT = 'descendant'
+ DESCENDANT_OR_SELF = 'descendant-or-self'
+ SELF = 'self'
+
+ @classmethod
+ def forname(cls, name):
+ """Return the axis constant for the given name, or `None` if no such
+ axis was defined.
+ """
+ return getattr(cls, name.upper().replace('-', '_'), None)
+
+
+ATTRIBUTE = Axis.ATTRIBUTE
+CHILD = Axis.CHILD
+DESCENDANT = Axis.DESCENDANT
+DESCENDANT_OR_SELF = Axis.DESCENDANT_OR_SELF
+SELF = Axis.SELF
+
+
+class GenericStrategy(object):
+
+ @classmethod
+ def supports(cls, path):
+ return True
+
+ def __init__(self, path):
+ self.path = path
+
+ def test(self, ignore_context):
+ p = self.path
+ if ignore_context:
+ if p[0][0] is ATTRIBUTE:
+ steps = [_DOTSLASHSLASH] + p
+ else:
+ steps = [(DESCENDANT_OR_SELF, p[0][1], p[0][2])] + p[1:]
+ elif p[0][0] is CHILD or p[0][0] is ATTRIBUTE \
+ or p[0][0] is DESCENDANT:
+ steps = [_DOTSLASH] + p
+ else:
+ steps = p
+
+ # for node it contains all positions of xpath expression
+ # where its child should start checking for matches
+ # with list of corresponding context counters
+ # there can be many of them, because position that is from
+ # descendant-like axis can be achieved from different nodes
+ # for example <a><a><b/></a></a> should match both //a//b[1]
+ # and //a//b[2]
+ # positions always form increasing sequence (invariant)
+ stack = [[(0, [[]])]]
+
+ def _test(event, namespaces, variables, updateonly=False):
+ kind, data, pos = event[:3]
+ retval = None
+
+ # Manage the stack that tells us "where we are" in the stream
+ if kind is END:
+ if stack:
+ stack.pop()
+ return None
+ if kind is START_NS or kind is END_NS \
+ or kind is START_CDATA or kind is END_CDATA:
+ # should we make namespaces work?
+ return None
+
+ pos_queue = deque([(pos, cou, []) for pos, cou in stack[-1]])
+ next_pos = []
+
+ # length of real part of path - we omit attribute axis
+ real_len = len(steps) - ((steps[-1][0] == ATTRIBUTE) or 1 and 0)
+ last_checked = -1
+
+ # places where we have to check for match, are these
+ # provided by parent
+ while pos_queue:
+ x, pcou, mcou = pos_queue.popleft()
+ axis, nodetest, predicates = steps[x]
+
+ # we need to push descendant-like positions from parent
+ # further
+ if (axis is DESCENDANT or axis is DESCENDANT_OR_SELF) and pcou:
+ if next_pos and next_pos[-1][0] == x:
+ next_pos[-1][1].extend(pcou)
+ else:
+ next_pos.append((x, pcou))
+
+ # nodetest first
+ if not nodetest(kind, data, pos, namespaces, variables):
+ continue
+
+ # counters packs that were already bad
+ missed = set()
+ counters_len = len(pcou) + len(mcou)
+
+ # number of counters - we have to create one
+ # for every context position based predicate
+ cnum = 0
+
+ # tells if we have match with position x
+ matched = True
+
+ if predicates:
+ for predicate in predicates:
+ pretval = predicate(kind, data, pos,
+ namespaces,
+ variables)
+ if type(pretval) is float: # FIXME <- need to check
+ # this for other types that
+ # can be coerced to float
+
+ # each counter pack needs to be checked
+ for i, cou in enumerate(chain(pcou, mcou)):
+ # it was bad before
+ if i in missed:
+ continue
+
+ if len(cou) < cnum + 1:
+ cou.append(0)
+ cou[cnum] += 1
+
+ # it is bad now
+ if cou[cnum] != int(pretval):
+ missed.add(i)
+
+ # none of counters pack was good
+ if len(missed) == counters_len:
+ pretval = False
+ cnum += 1
+
+ if not pretval:
+ matched = False
+ break
+
+ if not matched:
+ continue
+
+ # counter for next position with current node as context node
+ child_counter = []
+
+ if x + 1 == real_len:
+ # we reached end of expression, because x + 1
+ # is equal to the length of expression
+ matched = True
+ axis, nodetest, predicates = steps[-1]
+ if axis is ATTRIBUTE:
+ matched = nodetest(kind, data, pos, namespaces,
+ variables)
+ if matched:
+ retval = matched
+ else:
+ next_axis = steps[x + 1][0]
+
+ # if next axis allows matching self we have
+ # to add next position to our queue
+ if next_axis is DESCENDANT_OR_SELF or next_axis is SELF:
+ if not pos_queue or pos_queue[0][0] > x + 1:
+ pos_queue.appendleft((x + 1, [], [child_counter]))
+ else:
+ pos_queue[0][2].append(child_counter)
+
+ # if axis is not self we have to add it to child's list
+ if next_axis is not SELF:
+ next_pos.append((x + 1, [child_counter]))
+
+ if kind is START:
+ stack.append(next_pos)
+
+ return retval
+
+ return _test
+
+
+class SimplePathStrategy(object):
+ """Strategy for path with only local names, attributes and text nodes."""
+
+ @classmethod
+ def supports(cls, path):
+ if path[0][0] is ATTRIBUTE:
+ return False
+ allowed_tests = (LocalNameTest, CommentNodeTest, TextNodeTest)
+ for _, nodetest, predicates in path:
+ if predicates:
+ return False
+ if not isinstance(nodetest, allowed_tests):
+ return False
+ return True
+
+ def __init__(self, path):
+ # fragments is list of tuples (fragment, pi, attr, self_beginning)
+ # fragment is list of nodetests for fragment of path with only
+ # child:: axes between
+ # pi is KMP partial match table for this fragment
+ # attr is attribute nodetest if fragment ends with @ and None otherwise
+ # self_beginning is True if axis for first fragment element
+ # was self (first fragment) or descendant-or-self (farther fragment)
+ self.fragments = []
+
+ self_beginning = False
+ fragment = []
+
+ def nodes_equal(node1, node2):
+ """Tests if two node tests are equal"""
+ if type(node1) is not type(node2):
+ return False
+ if type(node1) == LocalNameTest:
+ return node1.name == node2.name
+ return True
+
+ def calculate_pi(f):
+ """KMP prefix calculation for table"""
+ # the indexes in prefix table are shifted by one
+ # in comparision with common implementations
+ # pi[i] = NORMAL_PI[i + 1]
+ if len(f) == 0:
+ return []
+ pi = [0]
+ s = 0
+ for i in range(1, len(f)):
+ while s > 0 and not nodes_equal(f[s], f[i]):
+ s = pi[s-1]
+ if nodes_equal(f[s], f[i]):
+ s += 1
+ pi.append(s)
+ return pi
+
+ for axis in path:
+ if axis[0] is SELF:
+ if len(fragment) != 0:
+ # if element is not first in fragment it has to be
+ # the same as previous one
+ # for example child::a/self::b is always wrong
+ if axis[1] != fragment[-1][1]:
+ self.fragments = None
+ return
+ else:
+ self_beginning = True
+ fragment.append(axis[1])
+ elif axis[0] is CHILD:
+ fragment.append(axis[1])
+ elif axis[0] is ATTRIBUTE:
+ pi = calculate_pi(fragment)
+ self.fragments.append((fragment, pi, axis[1], self_beginning))
+ # attribute has always to be at the end, so we can jump out
+ return
+ else:
+ pi = calculate_pi(fragment)
+ self.fragments.append((fragment, pi, None, self_beginning))
+ fragment = [axis[1]]
+ if axis[0] is DESCENDANT:
+ self_beginning = False
+ else: # DESCENDANT_OR_SELF
+ self_beginning = True
+ pi = calculate_pi(fragment)
+ self.fragments.append((fragment, pi, None, self_beginning))
+
+ def test(self, ignore_context):
+ # stack of triples (fid, p, ic)
+ # fid is index of current fragment
+ # p is position in this fragment
+ # ic is if we ignore context in this fragment
+ stack = []
+ stack_push = stack.append
+ stack_pop = stack.pop
+ frags = self.fragments
+ frags_len = len(frags)
+
+ def _test(event, namespaces, variables, updateonly=False):
+ # expression found impossible during init
+ if frags is None:
+ return None
+
+ kind, data, pos = event[:3]
+
+ # skip events we don't care about
+ if kind is END:
+ if stack:
+ stack_pop()
+ return None
+ if kind is START_NS or kind is END_NS \
+ or kind is START_CDATA or kind is END_CDATA:
+ return None
+
+ if not stack:
+ # root node, nothing on stack, special case
+ fid = 0
+ # skip empty fragments (there can be actually only one)
+ while not frags[fid][0]:
+ fid += 1
+ p = 0
+ # empty fragment means descendant node at beginning
+ ic = ignore_context or (fid > 0)
+
+ # expression can match first node, if first axis is self::,
+ # descendant-or-self:: or if ignore_context is True and
+ # axis is not descendant::
+ if not frags[fid][3] and (not ignore_context or fid > 0):
+ # axis is not self-beggining, we have to skip this node
+ stack_push((fid, p, ic))
+ return None
+ else:
+ # take position of parent
+ fid, p, ic = stack[-1]
+
+ if fid is not None and not ic:
+ # fragment not ignoring context - we can't jump back
+ frag, pi, attrib, _ = frags[fid]
+ frag_len = len(frag)
+
+ if p == frag_len:
+ # that probably means empty first fragment
+ pass
+ elif frag[p](kind, data, pos, namespaces, variables):
+ # match, so we can go further
+ p += 1
+ else:
+ # not matched, so there will be no match in subtree
+ fid, p = None, None
+
+ if p == frag_len and fid + 1 != frags_len:
+ # we made it to end of fragment, we can go to following
+ fid += 1
+ p = 0
+ ic = True
+
+ if fid is None:
+ # there was no match in fragment not ignoring context
+ if kind is START:
+ stack_push((fid, p, ic))
+ return None
+
+ if ic:
+ # we are in fragment ignoring context
+ while True:
+ frag, pi, attrib, _ = frags[fid]
+ frag_len = len(frag)
+
+ # KMP new "character"
+ while p > 0 and (p >= frag_len or not \
+ frag[p](kind, data, pos, namespaces, variables)):
+ p = pi[p-1]
+ if frag[p](kind, data, pos, namespaces, variables):
+ p += 1
+
+ if p == frag_len:
+ # end of fragment reached
+ if fid + 1 == frags_len:
+ # that was last fragment
+ break
+ else:
+ fid += 1
+ p = 0
+ ic = True
+ if not frags[fid][3]:
+ # next fragment not self-beginning
+ break
+ else:
+ break
+
+ if kind is START:
+ # we have to put new position on stack, for children
+
+ if not ic and fid + 1 == frags_len and p == frag_len:
+ # it is end of the only, not context ignoring fragment
+ # so there will be no matches in subtree
+ stack_push((None, None, ic))
+ else:
+ stack_push((fid, p, ic))
+
+ # have we reached the end of the last fragment?
+ if fid + 1 == frags_len and p == frag_len:
+ if attrib: # attribute ended path, return value
+ return attrib(kind, data, pos, namespaces, variables)
+ return True
+
+ return None
+
+ return _test
+
+
+class SingleStepStrategy(object):
+
+ @classmethod
+ def supports(cls, path):
+ return len(path) == 1
+
+ def __init__(self, path):
+ self.path = path
+
+ def test(self, ignore_context):
+ steps = self.path
+ if steps[0][0] is ATTRIBUTE:
+ steps = [_DOTSLASH] + steps
+ select_attr = steps[-1][0] is ATTRIBUTE and steps[-1][1] or None
+
+ # for every position in expression stores counters' list
+ # it is used for position based predicates
+ counters = []
+ depth = [0]
+
+ def _test(event, namespaces, variables, updateonly=False):
+ kind, data, pos = event[:3]
+
+ # Manage the stack that tells us "where we are" in the stream
+ if kind is END:
+ if not ignore_context:
+ depth[0] -= 1
+ return None
+ elif kind is START_NS or kind is END_NS \
+ or kind is START_CDATA or kind is END_CDATA:
+ # should we make namespaces work?
+ return None
+
+ if not ignore_context:
+ outside = (steps[0][0] is SELF and depth[0] != 0) \
+ or (steps[0][0] is CHILD and depth[0] != 1) \
+ or (steps[0][0] is DESCENDANT and depth[0] < 1)
+ if kind is START:
+ depth[0] += 1
+ if outside:
+ return None
+
+ axis, nodetest, predicates = steps[0]
+ if not nodetest(kind, data, pos, namespaces, variables):
+ return None
+
+ if predicates:
+ cnum = 0
+ for predicate in predicates:
+ pretval = predicate(kind, data, pos, namespaces, variables)
+ if type(pretval) is float: # FIXME <- need to check this
+ # for other types that can be
+ # coerced to float
+ if len(counters) < cnum + 1:
+ counters.append(0)
+ counters[cnum] += 1
+ if counters[cnum] != int(pretval):
+ pretval = False
+ cnum += 1
+ if not pretval:
+ return None
+
+ if select_attr:
+ return select_attr(kind, data, pos, namespaces, variables)
+
+ return True
+
+ return _test
+
+
+class Path(object):
+ """Implements basic XPath support on streams.
+
+ Instances of this class represent a "compiled" XPath expression, and
+ provide methods for testing the path against a stream, as well as
+ extracting a substream matching that path.
+ """
+
+ STRATEGIES = (SingleStepStrategy, SimplePathStrategy, GenericStrategy)
+
+ def __init__(self, text, filename=None, lineno=-1):
+ """Create the path object from a string.
+
+ :param text: the path expression
+ :param filename: the name of the file in which the path expression was
+ found (used in error messages)
+ :param lineno: the line on which the expression was found
+ """
+ self.source = text
+ self.paths = PathParser(text, filename, lineno).parse()
+ self.strategies = []
+ for path in self.paths:
+ for strategy_class in self.STRATEGIES:
+ if strategy_class.supports(path):
+ self.strategies.append(strategy_class(path))
+ break
+ else:
+ raise NotImplemented('No strategy found for path')
+
+ def __repr__(self):
+ paths = []
+ for path in self.paths:
+ steps = []
+ for axis, nodetest, predicates in path:
+ steps.append('%s::%s' % (axis, nodetest))
+ for predicate in predicates:
+ steps[-1] += '[%s]' % predicate
+ paths.append('/'.join(steps))
+ return '<%s "%s">' % (type(self).__name__, '|'.join(paths))
+
+ def select(self, stream, namespaces=None, variables=None):
+ """Returns a substream of the given stream that matches the path.
+
+ If there are no matches, this method returns an empty stream.
+
+ >>> from genshi.input import XML
+ >>> xml = XML('<root><elem><child>Text</child></elem></root>')
+
+ >>> print(Path('.//child').select(xml))
+ <child>Text</child>
+
+ >>> print(Path('.//child/text()').select(xml))
+ Text
+
+ :param stream: the stream to select from
+ :param namespaces: (optional) a mapping of namespace prefixes to URIs
+ :param variables: (optional) a mapping of variable names to values
+ :return: the substream matching the path, or an empty stream
+ :rtype: `Stream`
+ """
+ if namespaces is None:
+ namespaces = {}
+ if variables is None:
+ variables = {}
+ stream = iter(stream)
+ def _generate(stream=stream, ns=namespaces, vs=variables):
+ next = stream.next
+ test = self.test()
+ for event in stream:
+ result = test(event, ns, vs)
+ if result is True:
+ yield event
+ if event[0] is START:
+ depth = 1
+ while depth > 0:
+ subevent = next()
+ if subevent[0] is START:
+ depth += 1
+ elif subevent[0] is END:
+ depth -= 1
+ yield subevent
+ test(subevent, ns, vs, updateonly=True)
+ elif result:
+ yield result
+ return Stream(_generate(),
+ serializer=getattr(stream, 'serializer', None))
+
+ def test(self, ignore_context=False):
+ """Returns a function that can be used to track whether the path matches
+ a specific stream event.
+
+ The function returned expects the positional arguments ``event``,
+ ``namespaces`` and ``variables``. The first is a stream event, while the
+ latter two are a mapping of namespace prefixes to URIs, and a mapping
+ of variable names to values, respectively. In addition, the function
+ accepts an ``updateonly`` keyword argument that default to ``False``. If
+ it is set to ``True``, the function only updates its internal state,
+ but does not perform any tests or return a result.
+
+ If the path matches the event, the function returns the match (for
+ example, a `START` or `TEXT` event.) Otherwise, it returns ``None``.
+
+ >>> from genshi.input import XML
+ >>> xml = XML('<root><elem><child id="1"/></elem><child id="2"/></root>')
+ >>> test = Path('child').test()
+ >>> namespaces, variables = {}, {}
+ >>> for event in xml:
+ ... if test(event, namespaces, variables):
+ ... print('%s %r' % (event[0], event[1]))
+ START (QName('child'), Attrs([(QName('id'), u'2')]))
+
+ :param ignore_context: if `True`, the path is interpreted like a pattern
+ in XSLT, meaning for example that it will match
+ at any depth
+ :return: a function that can be used to test individual events in a
+ stream against the path
+ :rtype: ``function``
+ """
+ tests = [s.test(ignore_context) for s in self.strategies]
+ if len(tests) == 1:
+ return tests[0]
+
+ def _multi(event, namespaces, variables, updateonly=False):
+ retval = None
+ for test in tests:
+ val = test(event, namespaces, variables, updateonly=updateonly)
+ if retval is None:
+ retval = val
+ return retval
+ return _multi
+
+
+class PathSyntaxError(Exception):
+ """Exception raised when an XPath expression is syntactically incorrect."""
+
+ def __init__(self, message, filename=None, lineno=-1, offset=-1):
+ if filename:
+ message = '%s (%s, line %d)' % (message, filename, lineno)
+ Exception.__init__(self, message)
+ self.filename = filename
+ self.lineno = lineno
+ self.offset = offset
+
+
+class PathParser(object):
+ """Tokenizes and parses an XPath expression."""
+
+ _QUOTES = (("'", "'"), ('"', '"'))
+ _TOKENS = ('::', ':', '..', '.', '//', '/', '[', ']', '()', '(', ')', '@',
+ '=', '!=', '!', '|', ',', '>=', '>', '<=', '<', '$')
+ _tokenize = re.compile('("[^"]*")|(\'[^\']*\')|((?:\d+)?\.\d+)|(%s)|([^%s\s]+)|\s+' % (
+ '|'.join([re.escape(t) for t in _TOKENS]),
+ ''.join([re.escape(t[0]) for t in _TOKENS]))).findall
+
+ def __init__(self, text, filename=None, lineno=-1):
+ self.filename = filename
+ self.lineno = lineno
+ self.tokens = [t for t in [dqstr or sqstr or number or token or name
+ for dqstr, sqstr, number, token, name in
+ self._tokenize(text)] if t]
+ self.pos = 0
+
+ # Tokenizer
+
+ @property
+ def at_end(self):
+ return self.pos == len(self.tokens) - 1
+
+ @property
+ def cur_token(self):
+ return self.tokens[self.pos]
+
+ def next_token(self):
+ self.pos += 1
+ return self.tokens[self.pos]
+
+ def peek_token(self):
+ if not self.at_end:
+ return self.tokens[self.pos + 1]
+ return None
+
+ # Recursive descent parser
+
+ def parse(self):
+ """Parses the XPath expression and returns a list of location path
+ tests.
+
+ For union expressions (such as `*|text()`), this function returns one
+ test for each operand in the union. For patch expressions that don't
+ use the union operator, the function always returns a list of size 1.
+
+ Each path test in turn is a sequence of tests that correspond to the
+ location steps, each tuples of the form `(axis, testfunc, predicates)`
+ """
+ paths = [self._location_path()]
+ while self.cur_token == '|':
+ self.next_token()
+ paths.append(self._location_path())
+ if not self.at_end:
+ raise PathSyntaxError('Unexpected token %r after end of expression'
+ % self.cur_token, self.filename, self.lineno)
+ return paths
+
+ def _location_path(self):
+ steps = []
+ while True:
+ if self.cur_token.startswith('/'):
+ if not steps:
+ if self.cur_token == '//':
+ # hack to make //* match every node - also root
+ self.next_token()
+ axis, nodetest, predicates = self._location_step()
+ steps.append((DESCENDANT_OR_SELF, nodetest,
+ predicates))
+ if self.at_end or not self.cur_token.startswith('/'):
+ break
+ continue
+ else:
+ raise PathSyntaxError('Absolute location paths not '
+ 'supported', self.filename,
+ self.lineno)
+ elif self.cur_token == '//':
+ steps.append((DESCENDANT_OR_SELF, NodeTest(), []))
+ self.next_token()
+
+ axis, nodetest, predicates = self._location_step()
+ if not axis:
+ axis = CHILD
+ steps.append((axis, nodetest, predicates))
+ if self.at_end or not self.cur_token.startswith('/'):
+ break
+
+ return steps
+
+ def _location_step(self):
+ if self.cur_token == '@':
+ axis = ATTRIBUTE
+ self.next_token()
+ elif self.cur_token == '.':
+ axis = SELF
+ elif self.cur_token == '..':
+ raise PathSyntaxError('Unsupported axis "parent"', self.filename,
+ self.lineno)
+ elif self.peek_token() == '::':
+ axis = Axis.forname(self.cur_token)
+ if axis is None:
+ raise PathSyntaxError('Unsupport axis "%s"' % axis,
+ self.filename, self.lineno)
+ self.next_token()
+ self.next_token()
+ else:
+ axis = None
+ nodetest = self._node_test(axis or CHILD)
+ predicates = []
+ while self.cur_token == '[':
+ predicates.append(self._predicate())
+ return axis, nodetest, predicates
+
+ def _node_test(self, axis=None):
+ test = prefix = None
+ next_token = self.peek_token()
+ if next_token in ('(', '()'): # Node type test
+ test = self._node_type()
+
+ elif next_token == ':': # Namespace prefix
+ prefix = self.cur_token
+ self.next_token()
+ localname = self.next_token()
+ if localname == '*':
+ test = QualifiedPrincipalTypeTest(axis, prefix)
+ else:
+ test = QualifiedNameTest(axis, prefix, localname)
+
+ else: # Name test
+ if self.cur_token == '*':
+ test = PrincipalTypeTest(axis)
+ elif self.cur_token == '.':
+ test = NodeTest()
+ else:
+ test = LocalNameTest(axis, self.cur_token)
+
+ if not self.at_end:
+ self.next_token()
+ return test
+
+ def _node_type(self):
+ name = self.cur_token
+ self.next_token()
+
+ args = []
+ if self.cur_token != '()':
+ # The processing-instruction() function optionally accepts the
+ # name of the PI as argument, which must be a literal string
+ self.next_token() # (
+ if self.cur_token != ')':
+ string = self.cur_token
+ if (string[0], string[-1]) in self._QUOTES:
+ string = string[1:-1]
+ args.append(string)
+
+ cls = _nodetest_map.get(name)
+ if not cls:
+ raise PathSyntaxError('%s() not allowed here' % name, self.filename,
+ self.lineno)
+ return cls(*args)
+
+ def _predicate(self):
+ assert self.cur_token == '['
+ self.next_token()
+ expr = self._or_expr()
+ if self.cur_token != ']':
+ raise PathSyntaxError('Expected "]" to close predicate, '
+ 'but found "%s"' % self.cur_token,
+ self.filename, self.lineno)
+ if not self.at_end:
+ self.next_token()
+ return expr
+
+ def _or_expr(self):
+ expr = self._and_expr()
+ while self.cur_token == 'or':
+ self.next_token()
+ expr = OrOperator(expr, self._and_expr())
+ return expr
+
+ def _and_expr(self):
+ expr = self._equality_expr()
+ while self.cur_token == 'and':
+ self.next_token()
+ expr = AndOperator(expr, self._equality_expr())
+ return expr
+
+ def _equality_expr(self):
+ expr = self._relational_expr()
+ while self.cur_token in ('=', '!='):
+ op = _operator_map[self.cur_token]
+ self.next_token()
+ expr = op(expr, self._relational_expr())
+ return expr
+
+ def _relational_expr(self):
+ expr = self._sub_expr()
+ while self.cur_token in ('>', '>=', '<', '>='):
+ op = _operator_map[self.cur_token]
+ self.next_token()
+ expr = op(expr, self._sub_expr())
+ return expr
+
+ def _sub_expr(self):
+ token = self.cur_token
+ if token != '(':
+ return self._primary_expr()
+ self.next_token()
+ expr = self._or_expr()
+ if self.cur_token != ')':
+ raise PathSyntaxError('Expected ")" to close sub-expression, '
+ 'but found "%s"' % self.cur_token,
+ self.filename, self.lineno)
+ self.next_token()
+ return expr
+
+ def _primary_expr(self):
+ token = self.cur_token
+ if len(token) > 1 and (token[0], token[-1]) in self._QUOTES:
+ self.next_token()
+ return StringLiteral(token[1:-1])
+ elif token[0].isdigit() or token[0] == '.':
+ self.next_token()
+ return NumberLiteral(as_float(token))
+ elif token == '$':
+ token = self.next_token()
+ self.next_token()
+ return VariableReference(token)
+ elif not self.at_end and self.peek_token().startswith('('):
+ return self._function_call()
+ else:
+ axis = None
+ if token == '@':
+ axis = ATTRIBUTE
+ self.next_token()
+ return self._node_test(axis)
+
+ def _function_call(self):
+ name = self.cur_token
+ if self.next_token() == '()':
+ args = []
+ else:
+ assert self.cur_token == '('
+ self.next_token()
+ args = [self._or_expr()]
+ while self.cur_token == ',':
+ self.next_token()
+ args.append(self._or_expr())
+ if not self.cur_token == ')':
+ raise PathSyntaxError('Expected ")" to close function argument '
+ 'list, but found "%s"' % self.cur_token,
+ self.filename, self.lineno)
+ self.next_token()
+ cls = _function_map.get(name)
+ if not cls:
+ raise PathSyntaxError('Unsupported function "%s"' % name,
+ self.filename, self.lineno)
+ return cls(*args)
+
+
+# Type coercion
+
+def as_scalar(value):
+ """Convert value to a scalar. If a single element Attrs() object is passed
+ the value of the single attribute will be returned."""
+ if isinstance(value, Attrs):
+ assert len(value) == 1
+ return value[0][1]
+ else:
+ return value
+
+def as_float(value):
+ # FIXME - if value is a bool it will be coerced to 0.0 and consequently
+ # compared as a float. This is probably not ideal.
+ return float(as_scalar(value))
+
+def as_long(value):
+ return long(as_scalar(value))
+
+def as_string(value):
+ value = as_scalar(value)
+ if value is False:
+ return ''
+ return unicode(value)
+
+def as_bool(value):
+ return bool(as_scalar(value))
+
+
+# Node tests
+
+class PrincipalTypeTest(object):
+ """Node test that matches any event with the given principal type."""
+ __slots__ = ['principal_type']
+ def __init__(self, principal_type):
+ self.principal_type = principal_type
+ def __call__(self, kind, data, pos, namespaces, variables):
+ if kind is START:
+ if self.principal_type is ATTRIBUTE:
+ return data[1] or None
+ else:
+ return True
+ def __repr__(self):
+ return '*'
+
+class QualifiedPrincipalTypeTest(object):
+ """Node test that matches any event with the given principal type in a
+ specific namespace."""
+ __slots__ = ['principal_type', 'prefix']
+ def __init__(self, principal_type, prefix):
+ self.principal_type = principal_type
+ self.prefix = prefix
+ def __call__(self, kind, data, pos, namespaces, variables):
+ namespace = Namespace(namespaces.get(self.prefix))
+ if kind is START:
+ if self.principal_type is ATTRIBUTE and data[1]:
+ return Attrs([(name, value) for name, value in data[1]
+ if name in namespace]) or None
+ else:
+ return data[0] in namespace
+ def __repr__(self):
+ return '%s:*' % self.prefix
+
+class LocalNameTest(object):
+ """Node test that matches any event with the given principal type and
+ local name.
+ """
+ __slots__ = ['principal_type', 'name']
+ def __init__(self, principal_type, name):
+ self.principal_type = principal_type
+ self.name = name
+ def __call__(self, kind, data, pos, namespaces, variables):
+ if kind is START:
+ if self.principal_type is ATTRIBUTE and self.name in data[1]:
+ return Attrs([(self.name, data[1].get(self.name))])
+ else:
+ return data[0].localname == self.name
+ def __repr__(self):
+ return self.name
+
+class QualifiedNameTest(object):
+ """Node test that matches any event with the given principal type and
+ qualified name.
+ """
+ __slots__ = ['principal_type', 'prefix', 'name']
+ def __init__(self, principal_type, prefix, name):
+ self.principal_type = principal_type
+ self.prefix = prefix
+ self.name = name
+ def __call__(self, kind, data, pos, namespaces, variables):
+ qname = QName('%s}%s' % (namespaces.get(self.prefix), self.name))
+ if kind is START:
+ if self.principal_type is ATTRIBUTE and qname in data[1]:
+ return Attrs([(self.name, data[1].get(self.name))])
+ else:
+ return data[0] == qname
+ def __repr__(self):
+ return '%s:%s' % (self.prefix, self.name)
+
+class CommentNodeTest(object):
+ """Node test that matches any comment events."""
+ __slots__ = []
+ def __call__(self, kind, data, pos, namespaces, variables):
+ return kind is COMMENT
+ def __repr__(self):
+ return 'comment()'
+
+class NodeTest(object):
+ """Node test that matches any node."""
+ __slots__ = []
+ def __call__(self, kind, data, pos, namespaces, variables):
+ if kind is START:
+ return True
+ return kind, data, pos
+ def __repr__(self):
+ return 'node()'
+
+class ProcessingInstructionNodeTest(object):
+ """Node test that matches any processing instruction event."""
+ __slots__ = ['target']
+ def __init__(self, target=None):
+ self.target = target
+ def __call__(self, kind, data, pos, namespaces, variables):
+ return kind is PI and (not self.target or data[0] == self.target)
+ def __repr__(self):
+ arg = ''
+ if self.target:
+ arg = '"' + self.target + '"'
+ return 'processing-instruction(%s)' % arg
+
+class TextNodeTest(object):
+ """Node test that matches any text event."""
+ __slots__ = []
+ def __call__(self, kind, data, pos, namespaces, variables):
+ return kind is TEXT
+ def __repr__(self):
+ return 'text()'
+
+_nodetest_map = {'comment': CommentNodeTest, 'node': NodeTest,
+ 'processing-instruction': ProcessingInstructionNodeTest,
+ 'text': TextNodeTest}
+
+# Functions
+
+class Function(object):
+ """Base class for function nodes in XPath expressions."""
+
+class BooleanFunction(Function):
+ """The `boolean` function, which converts its argument to a boolean
+ value.
+ """
+ __slots__ = ['expr']
+ _return_type = bool
+ def __init__(self, expr):
+ self.expr = expr
+ def __call__(self, kind, data, pos, namespaces, variables):
+ val = self.expr(kind, data, pos, namespaces, variables)
+ return as_bool(val)
+ def __repr__(self):
+ return 'boolean(%r)' % self.expr
+
+class CeilingFunction(Function):
+ """The `ceiling` function, which returns the nearest lower integer number
+ for the given number.
+ """
+ __slots__ = ['number']
+ def __init__(self, number):
+ self.number = number
+ def __call__(self, kind, data, pos, namespaces, variables):
+ number = self.number(kind, data, pos, namespaces, variables)
+ return ceil(as_float(number))
+ def __repr__(self):
+ return 'ceiling(%r)' % self.number
+
+class ConcatFunction(Function):
+ """The `concat` function, which concatenates (joins) the variable number of
+ strings it gets as arguments.
+ """
+ __slots__ = ['exprs']
+ def __init__(self, *exprs):
+ self.exprs = exprs
+ def __call__(self, kind, data, pos, namespaces, variables):
+ strings = []
+ for item in [expr(kind, data, pos, namespaces, variables)
+ for expr in self.exprs]:
+ strings.append(as_string(item))
+ return ''.join(strings)
+ def __repr__(self):
+ return 'concat(%s)' % ', '.join([repr(expr) for expr in self.exprs])
+
+class ContainsFunction(Function):
+ """The `contains` function, which returns whether a string contains a given
+ substring.
+ """
+ __slots__ = ['string1', 'string2']
+ def __init__(self, string1, string2):
+ self.string1 = string1
+ self.string2 = string2
+ def __call__(self, kind, data, pos, namespaces, variables):
+ string1 = self.string1(kind, data, pos, namespaces, variables)
+ string2 = self.string2(kind, data, pos, namespaces, variables)
+ return as_string(string2) in as_string(string1)
+ def __repr__(self):
+ return 'contains(%r, %r)' % (self.string1, self.string2)
+
+class MatchesFunction(Function):
+ """The `matches` function, which returns whether a string matches a regular
+ expression.
+ """
+ __slots__ = ['string1', 'string2']
+ flag_mapping = {'s': re.S, 'm': re.M, 'i': re.I, 'x': re.X}
+
+ def __init__(self, string1, string2, flags=''):
+ self.string1 = string1
+ self.string2 = string2
+ self.flags = self._map_flags(flags)
+ def __call__(self, kind, data, pos, namespaces, variables):
+ string1 = as_string(self.string1(kind, data, pos, namespaces, variables))
+ string2 = as_string(self.string2(kind, data, pos, namespaces, variables))
+ return re.search(string2, string1, self.flags)
+ def _map_flags(self, flags):
+ return reduce(operator.or_,
+ [self.flag_map[flag] for flag in flags], re.U)
+ def __repr__(self):
+ return 'contains(%r, %r)' % (self.string1, self.string2)
+
+class FalseFunction(Function):
+ """The `false` function, which always returns the boolean `false` value."""
+ __slots__ = []
+ def __call__(self, kind, data, pos, namespaces, variables):
+ return False
+ def __repr__(self):
+ return 'false()'
+
+class FloorFunction(Function):
+ """The `ceiling` function, which returns the nearest higher integer number
+ for the given number.
+ """
+ __slots__ = ['number']
+ def __init__(self, number):
+ self.number = number
+ def __call__(self, kind, data, pos, namespaces, variables):
+ number = self.number(kind, data, pos, namespaces, variables)
+ return floor(as_float(number))
+ def __repr__(self):
+ return 'floor(%r)' % self.number
+
+class LocalNameFunction(Function):
+ """The `local-name` function, which returns the local name of the current
+ element.
+ """
+ __slots__ = []
+ def __call__(self, kind, data, pos, namespaces, variables):
+ if kind is START:
+ return data[0].localname
+ def __repr__(self):
+ return 'local-name()'
+
+class NameFunction(Function):
+ """The `name` function, which returns the qualified name of the current
+ element.
+ """
+ __slots__ = []
+ def __call__(self, kind, data, pos, namespaces, variables):
+ if kind is START:
+ return data[0]
+ def __repr__(self):
+ return 'name()'
+
+class NamespaceUriFunction(Function):
+ """The `namespace-uri` function, which returns the namespace URI of the
+ current element.
+ """
+ __slots__ = []
+ def __call__(self, kind, data, pos, namespaces, variables):
+ if kind is START:
+ return data[0].namespace
+ def __repr__(self):
+ return 'namespace-uri()'
+
+class NotFunction(Function):
+ """The `not` function, which returns the negated boolean value of its
+ argument.
+ """
+ __slots__ = ['expr']
+ def __init__(self, expr):
+ self.expr = expr
+ def __call__(self, kind, data, pos, namespaces, variables):
+ return not as_bool(self.expr(kind, data, pos, namespaces, variables))
+ def __repr__(self):
+ return 'not(%s)' % self.expr
+
+class NormalizeSpaceFunction(Function):
+ """The `normalize-space` function, which removes leading and trailing
+ whitespace in the given string, and replaces multiple adjacent whitespace
+ characters inside the string with a single space.
+ """
+ __slots__ = ['expr']
+ _normalize = re.compile(r'\s{2,}').sub
+ def __init__(self, expr):
+ self.expr = expr
+ def __call__(self, kind, data, pos, namespaces, variables):
+ string = self.expr(kind, data, pos, namespaces, variables)
+ return self._normalize(' ', as_string(string).strip())
+ def __repr__(self):
+ return 'normalize-space(%s)' % repr(self.expr)
+
+class NumberFunction(Function):
+ """The `number` function that converts its argument to a number."""
+ __slots__ = ['expr']
+ def __init__(self, expr):
+ self.expr = expr
+ def __call__(self, kind, data, pos, namespaces, variables):
+ val = self.expr(kind, data, pos, namespaces, variables)
+ return as_float(val)
+ def __repr__(self):
+ return 'number(%r)' % self.expr
+
+class RoundFunction(Function):
+ """The `round` function, which returns the nearest integer number for the
+ given number.
+ """
+ __slots__ = ['number']
+ def __init__(self, number):
+ self.number = number
+ def __call__(self, kind, data, pos, namespaces, variables):
+ number = self.number(kind, data, pos, namespaces, variables)
+ return round(as_float(number))
+ def __repr__(self):
+ return 'round(%r)' % self.number
+
+class StartsWithFunction(Function):
+ """The `starts-with` function that returns whether one string starts with
+ a given substring.
+ """
+ __slots__ = ['string1', 'string2']
+ def __init__(self, string1, string2):
+ self.string1 = string1
+ self.string2 = string2
+ def __call__(self, kind, data, pos, namespaces, variables):
+ string1 = self.string1(kind, data, pos, namespaces, variables)
+ string2 = self.string2(kind, data, pos, namespaces, variables)
+ return as_string(string1).startswith(as_string(string2))
+ def __repr__(self):
+ return 'starts-with(%r, %r)' % (self.string1, self.string2)
+
+class StringLengthFunction(Function):
+ """The `string-length` function that returns the length of the given
+ string.
+ """
+ __slots__ = ['expr']
+ def __init__(self, expr):
+ self.expr = expr
+ def __call__(self, kind, data, pos, namespaces, variables):
+ string = self.expr(kind, data, pos, namespaces, variables)
+ return len(as_string(string))
+ def __repr__(self):
+ return 'string-length(%r)' % self.expr
+
+class SubstringFunction(Function):
+ """The `substring` function that returns the part of a string that starts
+ at the given offset, and optionally limited to the given length.
+ """
+ __slots__ = ['string', 'start', 'length']
+ def __init__(self, string, start, length=None):
+ self.string = string
+ self.start = start
+ self.length = length
+ def __call__(self, kind, data, pos, namespaces, variables):
+ string = self.string(kind, data, pos, namespaces, variables)
+ start = self.start(kind, data, pos, namespaces, variables)
+ length = 0
+ if self.length is not None:
+ length = self.length(kind, data, pos, namespaces, variables)
+ return string[as_long(start):len(as_string(string)) - as_long(length)]
+ def __repr__(self):
+ if self.length is not None:
+ return 'substring(%r, %r, %r)' % (self.string, self.start,
+ self.length)
+ else:
+ return 'substring(%r, %r)' % (self.string, self.start)
+
+class SubstringAfterFunction(Function):
+ """The `substring-after` function that returns the part of a string that
+ is found after the given substring.
+ """
+ __slots__ = ['string1', 'string2']
+ def __init__(self, string1, string2):
+ self.string1 = string1
+ self.string2 = string2
+ def __call__(self, kind, data, pos, namespaces, variables):
+ string1 = as_string(self.string1(kind, data, pos, namespaces, variables))
+ string2 = as_string(self.string2(kind, data, pos, namespaces, variables))
+ index = string1.find(string2)
+ if index >= 0:
+ return string1[index + len(string2):]
+ return ''
+ def __repr__(self):
+ return 'substring-after(%r, %r)' % (self.string1, self.string2)
+
+class SubstringBeforeFunction(Function):
+ """The `substring-before` function that returns the part of a string that
+ is found before the given substring.
+ """
+ __slots__ = ['string1', 'string2']
+ def __init__(self, string1, string2):
+ self.string1 = string1
+ self.string2 = string2
+ def __call__(self, kind, data, pos, namespaces, variables):
+ string1 = as_string(self.string1(kind, data, pos, namespaces, variables))
+ string2 = as_string(self.string2(kind, data, pos, namespaces, variables))
+ index = string1.find(string2)
+ if index >= 0:
+ return string1[:index]
+ return ''
+ def __repr__(self):
+ return 'substring-after(%r, %r)' % (self.string1, self.string2)
+
+class TranslateFunction(Function):
+ """The `translate` function that translates a set of characters in a
+ string to target set of characters.
+ """
+ __slots__ = ['string', 'fromchars', 'tochars']
+ def __init__(self, string, fromchars, tochars):
+ self.string = string
+ self.fromchars = fromchars
+ self.tochars = tochars
+ def __call__(self, kind, data, pos, namespaces, variables):
+ string = as_string(self.string(kind, data, pos, namespaces, variables))
+ fromchars = as_string(self.fromchars(kind, data, pos, namespaces, variables))
+ tochars = as_string(self.tochars(kind, data, pos, namespaces, variables))
+ table = dict(zip([ord(c) for c in fromchars],
+ [ord(c) for c in tochars]))
+ return string.translate(table)
+ def __repr__(self):
+ return 'translate(%r, %r, %r)' % (self.string, self.fromchars,
+ self.tochars)
+
+class TrueFunction(Function):
+ """The `true` function, which always returns the boolean `true` value."""
+ __slots__ = []
+ def __call__(self, kind, data, pos, namespaces, variables):
+ return True
+ def __repr__(self):
+ return 'true()'
+
+_function_map = {'boolean': BooleanFunction, 'ceiling': CeilingFunction,
+ 'concat': ConcatFunction, 'contains': ContainsFunction,
+ 'matches': MatchesFunction, 'false': FalseFunction, 'floor':
+ FloorFunction, 'local-name': LocalNameFunction, 'name':
+ NameFunction, 'namespace-uri': NamespaceUriFunction,
+ 'normalize-space': NormalizeSpaceFunction, 'not': NotFunction,
+ 'number': NumberFunction, 'round': RoundFunction,
+ 'starts-with': StartsWithFunction, 'string-length':
+ StringLengthFunction, 'substring': SubstringFunction,
+ 'substring-after': SubstringAfterFunction, 'substring-before':
+ SubstringBeforeFunction, 'translate': TranslateFunction,
+ 'true': TrueFunction}
+
+# Literals & Variables
+
+class Literal(object):
+ """Abstract base class for literal nodes."""
+
+class StringLiteral(Literal):
+ """A string literal node."""
+ __slots__ = ['text']
+ def __init__(self, text):
+ self.text = text
+ def __call__(self, kind, data, pos, namespaces, variables):
+ return self.text
+ def __repr__(self):
+ return '"%s"' % self.text
+
+class NumberLiteral(Literal):
+ """A number literal node."""
+ __slots__ = ['number']
+ def __init__(self, number):
+ self.number = number
+ def __call__(self, kind, data, pos, namespaces, variables):
+ return self.number
+ def __repr__(self):
+ return str(self.number)
+
+class VariableReference(Literal):
+ """A variable reference node."""
+ __slots__ = ['name']
+ def __init__(self, name):
+ self.name = name
+ def __call__(self, kind, data, pos, namespaces, variables):
+ return variables.get(self.name)
+ def __repr__(self):
+ return str(self.name)
+
+# Operators
+
+class AndOperator(object):
+ """The boolean operator `and`."""
+ __slots__ = ['lval', 'rval']
+ def __init__(self, lval, rval):
+ self.lval = lval
+ self.rval = rval
+ def __call__(self, kind, data, pos, namespaces, variables):
+ lval = as_bool(self.lval(kind, data, pos, namespaces, variables))
+ if not lval:
+ return False
+ rval = self.rval(kind, data, pos, namespaces, variables)
+ return as_bool(rval)
+ def __repr__(self):
+ return '%s and %s' % (self.lval, self.rval)
+
+class EqualsOperator(object):
+ """The equality operator `=`."""
+ __slots__ = ['lval', 'rval']
+ def __init__(self, lval, rval):
+ self.lval = lval
+ self.rval = rval
+ def __call__(self, kind, data, pos, namespaces, variables):
+ lval = as_scalar(self.lval(kind, data, pos, namespaces, variables))
+ rval = as_scalar(self.rval(kind, data, pos, namespaces, variables))
+ return lval == rval
+ def __repr__(self):
+ return '%s=%s' % (self.lval, self.rval)
+
+class NotEqualsOperator(object):
+ """The equality operator `!=`."""
+ __slots__ = ['lval', 'rval']
+ def __init__(self, lval, rval):
+ self.lval = lval
+ self.rval = rval
+ def __call__(self, kind, data, pos, namespaces, variables):
+ lval = as_scalar(self.lval(kind, data, pos, namespaces, variables))
+ rval = as_scalar(self.rval(kind, data, pos, namespaces, variables))
+ return lval != rval
+ def __repr__(self):
+ return '%s!=%s' % (self.lval, self.rval)
+
+class OrOperator(object):
+ """The boolean operator `or`."""
+ __slots__ = ['lval', 'rval']
+ def __init__(self, lval, rval):
+ self.lval = lval
+ self.rval = rval
+ def __call__(self, kind, data, pos, namespaces, variables):
+ lval = as_bool(self.lval(kind, data, pos, namespaces, variables))
+ if lval:
+ return True
+ rval = self.rval(kind, data, pos, namespaces, variables)
+ return as_bool(rval)
+ def __repr__(self):
+ return '%s or %s' % (self.lval, self.rval)
+
+class GreaterThanOperator(object):
+ """The relational operator `>` (greater than)."""
+ __slots__ = ['lval', 'rval']
+ def __init__(self, lval, rval):
+ self.lval = lval
+ self.rval = rval
+ def __call__(self, kind, data, pos, namespaces, variables):
+ lval = self.lval(kind, data, pos, namespaces, variables)
+ rval = self.rval(kind, data, pos, namespaces, variables)
+ return as_float(lval) > as_float(rval)
+ def __repr__(self):
+ return '%s>%s' % (self.lval, self.rval)
+
+class GreaterThanOrEqualOperator(object):
+ """The relational operator `>=` (greater than or equal)."""
+ __slots__ = ['lval', 'rval']
+ def __init__(self, lval, rval):
+ self.lval = lval
+ self.rval = rval
+ def __call__(self, kind, data, pos, namespaces, variables):
+ lval = self.lval(kind, data, pos, namespaces, variables)
+ rval = self.rval(kind, data, pos, namespaces, variables)
+ return as_float(lval) >= as_float(rval)
+ def __repr__(self):
+ return '%s>=%s' % (self.lval, self.rval)
+
+class LessThanOperator(object):
+ """The relational operator `<` (less than)."""
+ __slots__ = ['lval', 'rval']
+ def __init__(self, lval, rval):
+ self.lval = lval
+ self.rval = rval
+ def __call__(self, kind, data, pos, namespaces, variables):
+ lval = self.lval(kind, data, pos, namespaces, variables)
+ rval = self.rval(kind, data, pos, namespaces, variables)
+ return as_float(lval) < as_float(rval)
+ def __repr__(self):
+ return '%s<%s' % (self.lval, self.rval)
+
+class LessThanOrEqualOperator(object):
+ """The relational operator `<=` (less than or equal)."""
+ __slots__ = ['lval', 'rval']
+ def __init__(self, lval, rval):
+ self.lval = lval
+ self.rval = rval
+ def __call__(self, kind, data, pos, namespaces, variables):
+ lval = self.lval(kind, data, pos, namespaces, variables)
+ rval = self.rval(kind, data, pos, namespaces, variables)
+ return as_float(lval) <= as_float(rval)
+ def __repr__(self):
+ return '%s<=%s' % (self.lval, self.rval)
+
+_operator_map = {'=': EqualsOperator, '!=': NotEqualsOperator,
+ '>': GreaterThanOperator, '>=': GreaterThanOrEqualOperator,
+ '<': LessThanOperator, '>=': LessThanOrEqualOperator}
+
+
+_DOTSLASHSLASH = (DESCENDANT_OR_SELF, PrincipalTypeTest(None), ())
+_DOTSLASH = (SELF, PrincipalTypeTest(None), ())
diff --git a/websdk/genshi/template/__init__.py b/websdk/genshi/template/__init__.py
new file mode 100644
index 0000000..47a9310
--- /dev/null
+++ b/websdk/genshi/template/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2007 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Implementation of the template engine."""
+
+from genshi.template.base import Context, Template, TemplateError, \
+ TemplateRuntimeError, TemplateSyntaxError, \
+ BadDirectiveError
+from genshi.template.loader import TemplateLoader, TemplateNotFound
+from genshi.template.markup import MarkupTemplate
+from genshi.template.text import TextTemplate, OldTextTemplate, NewTextTemplate
+
+__docformat__ = 'restructuredtext en'
diff --git a/websdk/genshi/template/_ast24.py b/websdk/genshi/template/_ast24.py
new file mode 100644
index 0000000..05d241b
--- /dev/null
+++ b/websdk/genshi/template/_ast24.py
@@ -0,0 +1,446 @@
+# Generated automatically, please do not edit
+# Generator can be found in Genshi SVN, scripts/ast-generator.py
+
+__version__ = 43614
+
+class AST(object):
+ _fields = None
+ __doc__ = None
+
+class operator(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+class Add(operator):
+ _fields = None
+ __doc__ = None
+
+class boolop(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+class And(boolop):
+ _fields = None
+ __doc__ = None
+
+class stmt(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = ['lineno', 'col_offset']
+class Assert(stmt):
+ _fields = ('test', 'msg')
+ __doc__ = None
+
+class Assign(stmt):
+ _fields = ('targets', 'value')
+ __doc__ = None
+
+class expr(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = ['lineno', 'col_offset']
+class Attribute(expr):
+ _fields = ('value', 'attr', 'ctx')
+ __doc__ = None
+
+class AugAssign(stmt):
+ _fields = ('target', 'op', 'value')
+ __doc__ = None
+
+class expr_context(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+class AugLoad(expr_context):
+ _fields = None
+ __doc__ = None
+
+class AugStore(expr_context):
+ _fields = None
+ __doc__ = None
+
+class BinOp(expr):
+ _fields = ('left', 'op', 'right')
+ __doc__ = None
+
+class BitAnd(operator):
+ _fields = None
+ __doc__ = None
+
+class BitOr(operator):
+ _fields = None
+ __doc__ = None
+
+class BitXor(operator):
+ _fields = None
+ __doc__ = None
+
+class BoolOp(expr):
+ _fields = ('op', 'values')
+ __doc__ = None
+
+class Break(stmt):
+ _fields = None
+ __doc__ = None
+
+class Call(expr):
+ _fields = ('func', 'args', 'keywords', 'starargs', 'kwargs')
+ __doc__ = None
+
+class ClassDef(stmt):
+ _fields = ('name', 'bases', 'body')
+ __doc__ = None
+
+class Compare(expr):
+ _fields = ('left', 'ops', 'comparators')
+ __doc__ = None
+
+class Continue(stmt):
+ _fields = None
+ __doc__ = None
+
+class Del(expr_context):
+ _fields = None
+ __doc__ = None
+
+class Delete(stmt):
+ _fields = ('targets',)
+ __doc__ = None
+
+class Dict(expr):
+ _fields = ('keys', 'values')
+ __doc__ = None
+
+class Div(operator):
+ _fields = None
+ __doc__ = None
+
+class slice(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+class Ellipsis(slice):
+ _fields = None
+ __doc__ = None
+
+class cmpop(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+class Eq(cmpop):
+ _fields = None
+ __doc__ = None
+
+class Exec(stmt):
+ _fields = ('body', 'globals', 'locals')
+ __doc__ = None
+
+class Expr(stmt):
+ _fields = ('value',)
+ __doc__ = None
+
+class mod(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+class Expression(mod):
+ _fields = ('body',)
+ __doc__ = None
+
+class ExtSlice(slice):
+ _fields = ('dims',)
+ __doc__ = None
+
+class FloorDiv(operator):
+ _fields = None
+ __doc__ = None
+
+class For(stmt):
+ _fields = ('target', 'iter', 'body', 'orelse')
+ __doc__ = None
+
+class FunctionDef(stmt):
+ _fields = ('name', 'args', 'body', 'decorators')
+ __doc__ = None
+
+class GeneratorExp(expr):
+ _fields = ('elt', 'generators')
+ __doc__ = None
+
+class Global(stmt):
+ _fields = ('names',)
+ __doc__ = None
+
+class Gt(cmpop):
+ _fields = None
+ __doc__ = None
+
+class GtE(cmpop):
+ _fields = None
+ __doc__ = None
+
+class If(stmt):
+ _fields = ('test', 'body', 'orelse')
+ __doc__ = None
+
+class IfExp(expr):
+ _fields = ('test', 'body', 'orelse')
+ __doc__ = None
+
+class Import(stmt):
+ _fields = ('names',)
+ __doc__ = None
+
+class ImportFrom(stmt):
+ _fields = ('module', 'names', 'level')
+ __doc__ = None
+
+class In(cmpop):
+ _fields = None
+ __doc__ = None
+
+class Index(slice):
+ _fields = ('value',)
+ __doc__ = None
+
+class Interactive(mod):
+ _fields = ('body',)
+ __doc__ = None
+
+class unaryop(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+class Invert(unaryop):
+ _fields = None
+ __doc__ = None
+
+class Is(cmpop):
+ _fields = None
+ __doc__ = None
+
+class IsNot(cmpop):
+ _fields = None
+ __doc__ = None
+
+class LShift(operator):
+ _fields = None
+ __doc__ = None
+
+class Lambda(expr):
+ _fields = ('args', 'body')
+ __doc__ = None
+
+class List(expr):
+ _fields = ('elts', 'ctx')
+ __doc__ = None
+
+class ListComp(expr):
+ _fields = ('elt', 'generators')
+ __doc__ = None
+
+class Load(expr_context):
+ _fields = None
+ __doc__ = None
+
+class Lt(cmpop):
+ _fields = None
+ __doc__ = None
+
+class LtE(cmpop):
+ _fields = None
+ __doc__ = None
+
+class Mod(operator):
+ _fields = None
+ __doc__ = None
+
+class Module(mod):
+ _fields = ('body',)
+ __doc__ = None
+
+class Mult(operator):
+ _fields = None
+ __doc__ = None
+
+class Name(expr):
+ _fields = ('id', 'ctx')
+ __doc__ = None
+
+class Not(unaryop):
+ _fields = None
+ __doc__ = None
+
+class NotEq(cmpop):
+ _fields = None
+ __doc__ = None
+
+class NotIn(cmpop):
+ _fields = None
+ __doc__ = None
+
+class Num(expr):
+ _fields = ('n',)
+ __doc__ = None
+
+class Or(boolop):
+ _fields = None
+ __doc__ = None
+
+class Param(expr_context):
+ _fields = None
+ __doc__ = None
+
+class Pass(stmt):
+ _fields = None
+ __doc__ = None
+
+class Pow(operator):
+ _fields = None
+ __doc__ = None
+
+class Print(stmt):
+ _fields = ('dest', 'values', 'nl')
+ __doc__ = None
+
+class RShift(operator):
+ _fields = None
+ __doc__ = None
+
+class Raise(stmt):
+ _fields = ('type', 'inst', 'tback')
+ __doc__ = None
+
+class Repr(expr):
+ _fields = ('value',)
+ __doc__ = None
+
+class Return(stmt):
+ _fields = ('value',)
+ __doc__ = None
+
+class Slice(slice):
+ _fields = ('lower', 'upper', 'step')
+ __doc__ = None
+
+class Store(expr_context):
+ _fields = None
+ __doc__ = None
+
+class Str(expr):
+ _fields = ('s',)
+ __doc__ = None
+
+class Sub(operator):
+ _fields = None
+ __doc__ = None
+
+class Subscript(expr):
+ _fields = ('value', 'slice', 'ctx')
+ __doc__ = None
+
+class Suite(mod):
+ _fields = ('body',)
+ __doc__ = None
+
+class TryExcept(stmt):
+ _fields = ('body', 'handlers', 'orelse')
+ __doc__ = None
+
+class TryFinally(stmt):
+ _fields = ('body', 'finalbody')
+ __doc__ = None
+
+class Tuple(expr):
+ _fields = ('elts', 'ctx')
+ __doc__ = None
+
+class UAdd(unaryop):
+ _fields = None
+ __doc__ = None
+
+class USub(unaryop):
+ _fields = None
+ __doc__ = None
+
+class UnaryOp(expr):
+ _fields = ('op', 'operand')
+ __doc__ = None
+
+class While(stmt):
+ _fields = ('test', 'body', 'orelse')
+ __doc__ = None
+
+class With(stmt):
+ _fields = ('context_expr', 'optional_vars', 'body')
+ __doc__ = None
+
+class Yield(expr):
+ _fields = ('value',)
+ __doc__ = None
+
+class alias(AST):
+ _fields = ('name', 'asname')
+ __doc__ = None
+
+class arguments(AST):
+ _fields = ('args', 'vararg', 'kwarg', 'defaults')
+ __doc__ = None
+
+class boolop(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+
+class cmpop(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+
+class comprehension(AST):
+ _fields = ('target', 'iter', 'ifs')
+ __doc__ = None
+
+class excepthandler(AST):
+ _fields = ('type', 'name', 'body', 'lineno', 'col_offset')
+ __doc__ = None
+
+class expr(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = ['lineno', 'col_offset']
+
+class expr_context(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+
+class keyword(AST):
+ _fields = ('arg', 'value')
+ __doc__ = None
+
+class mod(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+
+class operator(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+
+class slice(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+
+class stmt(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = ['lineno', 'col_offset']
+
+class unaryop(AST):
+ _fields = None
+ __doc__ = None
+ _attributes = []
+
diff --git a/websdk/genshi/template/ast24.py b/websdk/genshi/template/ast24.py
new file mode 100644
index 0000000..af6dce9
--- /dev/null
+++ b/websdk/genshi/template/ast24.py
@@ -0,0 +1,505 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Emulation of the proper abstract syntax tree API for Python 2.4."""
+
+import compiler
+import compiler.ast
+
+from genshi.template import _ast24 as _ast
+
+__all__ = ['_ast', 'parse']
+__docformat__ = 'restructuredtext en'
+
+
+def _new(cls, *args, **kwargs):
+ ret = cls()
+ if ret._fields:
+ for attr, value in zip(ret._fields, args):
+ if attr in kwargs:
+ raise ValueError('Field set both in args and kwargs')
+ setattr(ret, attr, value)
+ for attr in kwargs:
+ if (getattr(ret, '_fields', None) and attr in ret._fields) \
+ or (getattr(ret, '_attributes', None) and
+ attr in ret._attributes):
+ setattr(ret, attr, kwargs[attr])
+ return ret
+
+
+class ASTUpgrader(object):
+ """Transformer changing structure of Python 2.4 ASTs to
+ Python 2.5 ones.
+
+ Transforms ``compiler.ast`` Abstract Syntax Tree to builtin ``_ast``.
+ It can use fake`` _ast`` classes and this way allow ``_ast`` emulation
+ in Python 2.4.
+ """
+
+ def __init__(self):
+ self.out_flags = None
+ self.lines = [-1]
+
+ def _new(self, *args, **kwargs):
+ return _new(lineno = self.lines[-1], *args, **kwargs)
+
+ def visit(self, node):
+ if node is None:
+ return None
+ if type(node) is tuple:
+ return tuple([self.visit(n) for n in node])
+ lno = getattr(node, 'lineno', None)
+ if lno is not None:
+ self.lines.append(lno)
+ visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None)
+ if visitor is None:
+ raise Exception('Unhandled node type %r' % type(node))
+
+ retval = visitor(node)
+ if lno is not None:
+ self.lines.pop()
+ return retval
+
+ def visit_Module(self, node):
+ body = self.visit(node.node)
+ if node.doc:
+ body = [self._new(_ast.Expr, self._new(_ast.Str, node.doc))] + body
+ return self._new(_ast.Module, body)
+
+ def visit_Expression(self, node):
+ return self._new(_ast.Expression, self.visit(node.node))
+
+ def _extract_args(self, node):
+ tab = node.argnames[:]
+ if node.flags & compiler.ast.CO_VARKEYWORDS:
+ kwarg = tab[-1]
+ tab = tab[:-1]
+ else:
+ kwarg = None
+
+ if node.flags & compiler.ast.CO_VARARGS:
+ vararg = tab[-1]
+ tab = tab[:-1]
+ else:
+ vararg = None
+
+ def _tup(t):
+ if isinstance(t, str):
+ return self._new(_ast.Name, t, _ast.Store())
+ elif isinstance(t, tuple):
+ elts = [_tup(x) for x in t]
+ return self._new(_ast.Tuple, elts, _ast.Store())
+ else:
+ raise NotImplemented
+
+ args = []
+ for arg in tab:
+ if isinstance(arg, str):
+ args.append(self._new(_ast.Name, arg, _ast.Param()))
+ elif isinstance(arg, tuple):
+ args.append(_tup(arg))
+ else:
+ assert False, node.__class__
+
+ defaults = [self.visit(d) for d in node.defaults]
+ return self._new(_ast.arguments, args, vararg, kwarg, defaults)
+
+
+ def visit_Function(self, node):
+ if getattr(node, 'decorators', ()):
+ decorators = [self.visit(d) for d in node.decorators.nodes]
+ else:
+ decorators = []
+
+ args = self._extract_args(node)
+ body = self.visit(node.code)
+ if node.doc:
+ body = [self._new(_ast.Expr, self._new(_ast.Str, node.doc))] + body
+ return self._new(_ast.FunctionDef, node.name, args, body, decorators)
+
+ def visit_Class(self, node):
+ #self.name_types.append(_ast.Load)
+ bases = [self.visit(b) for b in node.bases]
+ #self.name_types.pop()
+ body = self.visit(node.code)
+ if node.doc:
+ body = [self._new(_ast.Expr, self._new(_ast.Str, node.doc))] + body
+ return self._new(_ast.ClassDef, node.name, bases, body)
+
+ def visit_Return(self, node):
+ return self._new(_ast.Return, self.visit(node.value))
+
+ def visit_Assign(self, node):
+ #self.name_types.append(_ast.Store)
+ targets = [self.visit(t) for t in node.nodes]
+ #self.name_types.pop()
+ return self._new(_ast.Assign, targets, self.visit(node.expr))
+
+ aug_operators = {
+ '+=': _ast.Add,
+ '/=': _ast.Div,
+ '//=': _ast.FloorDiv,
+ '<<=': _ast.LShift,
+ '%=': _ast.Mod,
+ '*=': _ast.Mult,
+ '**=': _ast.Pow,
+ '>>=': _ast.RShift,
+ '-=': _ast.Sub,
+ }
+
+ def visit_AugAssign(self, node):
+ target = self.visit(node.node)
+
+ # Because it's AugAssign target can't be list nor tuple
+ # so we only have to change context of one node
+ target.ctx = _ast.Store()
+ op = self.aug_operators[node.op]()
+ return self._new(_ast.AugAssign, target, op, self.visit(node.expr))
+
+ def _visit_Print(nl):
+ def _visit(self, node):
+ values = [self.visit(v) for v in node.nodes]
+ return self._new(_ast.Print, self.visit(node.dest), values, nl)
+ return _visit
+
+ visit_Print = _visit_Print(False)
+ visit_Printnl = _visit_Print(True)
+ del _visit_Print
+
+ def visit_For(self, node):
+ return self._new(_ast.For, self.visit(node.assign), self.visit(node.list),
+ self.visit(node.body), self.visit(node.else_))
+
+ def visit_While(self, node):
+ return self._new(_ast.While, self.visit(node.test), self.visit(node.body),
+ self.visit(node.else_))
+
+ def visit_If(self, node):
+ def _level(tests, else_):
+ test = self.visit(tests[0][0])
+ body = self.visit(tests[0][1])
+ if len(tests) == 1:
+ orelse = self.visit(else_)
+ else:
+ orelse = [_level(tests[1:], else_)]
+ return self._new(_ast.If, test, body, orelse)
+ return _level(node.tests, node.else_)
+
+ def visit_With(self, node):
+ return self._new(_ast.With, self.visit(node.expr),
+ self.visit(node.vars), self.visit(node.body))
+
+ def visit_Raise(self, node):
+ return self._new(_ast.Raise, self.visit(node.expr1),
+ self.visit(node.expr2), self.visit(node.expr3))
+
+ def visit_TryExcept(self, node):
+ handlers = []
+ for type, name, body in node.handlers:
+ handlers.append(self._new(_ast.excepthandler, self.visit(type),
+ self.visit(name), self.visit(body)))
+ return self._new(_ast.TryExcept, self.visit(node.body),
+ handlers, self.visit(node.else_))
+
+ def visit_TryFinally(self, node):
+ return self._new(_ast.TryFinally, self.visit(node.body),
+ self.visit(node.final))
+
+ def visit_Assert(self, node):
+ return self._new(_ast.Assert, self.visit(node.test), self.visit(node.fail))
+
+ def visit_Import(self, node):
+ names = [self._new(_ast.alias, n[0], n[1]) for n in node.names]
+ return self._new(_ast.Import, names)
+
+ def visit_From(self, node):
+ names = [self._new(_ast.alias, n[0], n[1]) for n in node.names]
+ return self._new(_ast.ImportFrom, node.modname, names, 0)
+
+ def visit_Exec(self, node):
+ return self._new(_ast.Exec, self.visit(node.expr),
+ self.visit(node.locals), self.visit(node.globals))
+
+ def visit_Global(self, node):
+ return self._new(_ast.Global, node.names[:])
+
+ def visit_Discard(self, node):
+ return self._new(_ast.Expr, self.visit(node.expr))
+
+ def _map_class(to):
+ def _visit(self, node):
+ return self._new(to)
+ return _visit
+
+ visit_Pass = _map_class(_ast.Pass)
+ visit_Break = _map_class(_ast.Break)
+ visit_Continue = _map_class(_ast.Continue)
+
+ def _visit_BinOperator(opcls):
+ def _visit(self, node):
+ return self._new(_ast.BinOp, self.visit(node.left),
+ opcls(), self.visit(node.right))
+ return _visit
+ visit_Add = _visit_BinOperator(_ast.Add)
+ visit_Div = _visit_BinOperator(_ast.Div)
+ visit_FloorDiv = _visit_BinOperator(_ast.FloorDiv)
+ visit_LeftShift = _visit_BinOperator(_ast.LShift)
+ visit_Mod = _visit_BinOperator(_ast.Mod)
+ visit_Mul = _visit_BinOperator(_ast.Mult)
+ visit_Power = _visit_BinOperator(_ast.Pow)
+ visit_RightShift = _visit_BinOperator(_ast.RShift)
+ visit_Sub = _visit_BinOperator(_ast.Sub)
+ del _visit_BinOperator
+
+ def _visit_BitOperator(opcls):
+ def _visit(self, node):
+ def _make(nodes):
+ if len(nodes) == 1:
+ return self.visit(nodes[0])
+ left = _make(nodes[:-1])
+ right = self.visit(nodes[-1])
+ return self._new(_ast.BinOp, left, opcls(), right)
+ return _make(node.nodes)
+ return _visit
+ visit_Bitand = _visit_BitOperator(_ast.BitAnd)
+ visit_Bitor = _visit_BitOperator(_ast.BitOr)
+ visit_Bitxor = _visit_BitOperator(_ast.BitXor)
+ del _visit_BitOperator
+
+ def _visit_UnaryOperator(opcls):
+ def _visit(self, node):
+ return self._new(_ast.UnaryOp, opcls(), self.visit(node.expr))
+ return _visit
+
+ visit_Invert = _visit_UnaryOperator(_ast.Invert)
+ visit_Not = _visit_UnaryOperator(_ast.Not)
+ visit_UnaryAdd = _visit_UnaryOperator(_ast.UAdd)
+ visit_UnarySub = _visit_UnaryOperator(_ast.USub)
+ del _visit_UnaryOperator
+
+ def _visit_BoolOperator(opcls):
+ def _visit(self, node):
+ values = [self.visit(n) for n in node.nodes]
+ return self._new(_ast.BoolOp, opcls(), values)
+ return _visit
+ visit_And = _visit_BoolOperator(_ast.And)
+ visit_Or = _visit_BoolOperator(_ast.Or)
+ del _visit_BoolOperator
+
+ cmp_operators = {
+ '==': _ast.Eq,
+ '!=': _ast.NotEq,
+ '<': _ast.Lt,
+ '<=': _ast.LtE,
+ '>': _ast.Gt,
+ '>=': _ast.GtE,
+ 'is': _ast.Is,
+ 'is not': _ast.IsNot,
+ 'in': _ast.In,
+ 'not in': _ast.NotIn,
+ }
+
+ def visit_Compare(self, node):
+ left = self.visit(node.expr)
+ ops = []
+ comparators = []
+ for optype, expr in node.ops:
+ ops.append(self.cmp_operators[optype]())
+ comparators.append(self.visit(expr))
+ return self._new(_ast.Compare, left, ops, comparators)
+
+ def visit_Lambda(self, node):
+ args = self._extract_args(node)
+ body = self.visit(node.code)
+ return self._new(_ast.Lambda, args, body)
+
+ def visit_IfExp(self, node):
+ return self._new(_ast.IfExp, self.visit(node.test), self.visit(node.then),
+ self.visit(node.else_))
+
+ def visit_Dict(self, node):
+ keys = [self.visit(x[0]) for x in node.items]
+ values = [self.visit(x[1]) for x in node.items]
+ return self._new(_ast.Dict, keys, values)
+
+ def visit_ListComp(self, node):
+ generators = [self.visit(q) for q in node.quals]
+ return self._new(_ast.ListComp, self.visit(node.expr), generators)
+
+ def visit_GenExprInner(self, node):
+ generators = [self.visit(q) for q in node.quals]
+ return self._new(_ast.GeneratorExp, self.visit(node.expr), generators)
+
+ def visit_GenExpr(self, node):
+ return self.visit(node.code)
+
+ def visit_GenExprFor(self, node):
+ ifs = [self.visit(i) for i in node.ifs]
+ return self._new(_ast.comprehension, self.visit(node.assign),
+ self.visit(node.iter), ifs)
+
+ def visit_ListCompFor(self, node):
+ ifs = [self.visit(i) for i in node.ifs]
+ return self._new(_ast.comprehension, self.visit(node.assign),
+ self.visit(node.list), ifs)
+
+ def visit_GenExprIf(self, node):
+ return self.visit(node.test)
+ visit_ListCompIf = visit_GenExprIf
+
+ def visit_Yield(self, node):
+ return self._new(_ast.Yield, self.visit(node.value))
+
+ def visit_CallFunc(self, node):
+ args = []
+ keywords = []
+ for arg in node.args:
+ if isinstance(arg, compiler.ast.Keyword):
+ keywords.append(self._new(_ast.keyword, arg.name,
+ self.visit(arg.expr)))
+ else:
+ args.append(self.visit(arg))
+ return self._new(_ast.Call, self.visit(node.node), args, keywords,
+ self.visit(node.star_args), self.visit(node.dstar_args))
+
+ def visit_Backquote(self, node):
+ return self._new(_ast.Repr, self.visit(node.expr))
+
+ def visit_Const(self, node):
+ if node.value is None: # appears in slices
+ return None
+ elif isinstance(node.value, basestring):
+ return self._new(_ast.Str, node.value)
+ else:
+ return self._new(_ast.Num, node.value)
+
+ def visit_Name(self, node):
+ return self._new(_ast.Name, node.name, _ast.Load())
+
+ def visit_Getattr(self, node):
+ return self._new(_ast.Attribute, self.visit(node.expr), node.attrname,
+ _ast.Load())
+
+ def visit_Tuple(self, node):
+ nodes = [self.visit(n) for n in node.nodes]
+ return self._new(_ast.Tuple, nodes, _ast.Load())
+
+ def visit_List(self, node):
+ nodes = [self.visit(n) for n in node.nodes]
+ return self._new(_ast.List, nodes, _ast.Load())
+
+ def get_ctx(self, flags):
+ if flags == 'OP_DELETE':
+ return _ast.Del()
+ elif flags == 'OP_APPLY':
+ return _ast.Load()
+ elif flags == 'OP_ASSIGN':
+ return _ast.Store()
+ else:
+ # FIXME Exception here
+ assert False, repr(flags)
+
+ def visit_AssName(self, node):
+ self.out_flags = node.flags
+ ctx = self.get_ctx(node.flags)
+ return self._new(_ast.Name, node.name, ctx)
+
+ def visit_AssAttr(self, node):
+ self.out_flags = node.flags
+ ctx = self.get_ctx(node.flags)
+ return self._new(_ast.Attribute, self.visit(node.expr),
+ node.attrname, ctx)
+
+ def _visit_AssCollection(cls):
+ def _visit(self, node):
+ flags = None
+ elts = []
+ for n in node.nodes:
+ elts.append(self.visit(n))
+ if flags is None:
+ flags = self.out_flags
+ else:
+ assert flags == self.out_flags
+ self.out_flags = flags
+ ctx = self.get_ctx(flags)
+ return self._new(cls, elts, ctx)
+ return _visit
+
+ visit_AssList = _visit_AssCollection(_ast.List)
+ visit_AssTuple = _visit_AssCollection(_ast.Tuple)
+ del _visit_AssCollection
+
+ def visit_Slice(self, node):
+ lower = self.visit(node.lower)
+ upper = self.visit(node.upper)
+ ctx = self.get_ctx(node.flags)
+ self.out_flags = node.flags
+ return self._new(_ast.Subscript, self.visit(node.expr),
+ self._new(_ast.Slice, lower, upper, None), ctx)
+
+ def visit_Subscript(self, node):
+ ctx = self.get_ctx(node.flags)
+ subs = [self.visit(s) for s in node.subs]
+
+ advanced = (_ast.Slice, _ast.Ellipsis)
+ slices = []
+ nonindex = False
+ for sub in subs:
+ if isinstance(sub, advanced):
+ nonindex = True
+ slices.append(sub)
+ else:
+ slices.append(self._new(_ast.Index, sub))
+ if len(slices) == 1:
+ slice = slices[0]
+ elif nonindex:
+ slice = self._new(_ast.ExtSlice, slices)
+ else:
+ slice = self._new(_ast.Tuple, slices, _ast.Load())
+
+ self.out_flags = node.flags
+ return self._new(_ast.Subscript, self.visit(node.expr), slice, ctx)
+
+ def visit_Sliceobj(self, node):
+ a = [self.visit(n) for n in node.nodes + [None]*(3 - len(node.nodes))]
+ return self._new(_ast.Slice, a[0], a[1], a[2])
+
+ def visit_Ellipsis(self, node):
+ return self._new(_ast.Ellipsis)
+
+ def visit_Stmt(self, node):
+ def _check_del(n):
+ # del x is just AssName('x', 'OP_DELETE')
+ # we want to transform it to Delete([Name('x', Del())])
+ dcls = (_ast.Name, _ast.List, _ast.Subscript, _ast.Attribute)
+ if isinstance(n, dcls) and isinstance(n.ctx, _ast.Del):
+ return self._new(_ast.Delete, [n])
+ elif isinstance(n, _ast.Tuple) and isinstance(n.ctx, _ast.Del):
+ # unpack last tuple to avoid making del (x, y, z,);
+ # out of del x, y, z; (there's no difference between
+ # this two in compiler.ast)
+ return self._new(_ast.Delete, n.elts)
+ else:
+ return n
+ def _keep(n):
+ if isinstance(n, _ast.Expr) and n.value is None:
+ return False
+ else:
+ return True
+ return [s for s in [_check_del(self.visit(n)) for n in node.nodes]
+ if _keep(s)]
+
+
+def parse(source, mode):
+ node = compiler.parse(source, mode)
+ return ASTUpgrader().visit(node)
diff --git a/websdk/genshi/template/astutil.py b/websdk/genshi/template/astutil.py
new file mode 100644
index 0000000..c3ad107
--- /dev/null
+++ b/websdk/genshi/template/astutil.py
@@ -0,0 +1,784 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2008-2010 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Support classes for generating code from abstract syntax trees."""
+
+try:
+ import _ast
+except ImportError:
+ from genshi.template.ast24 import _ast, parse
+else:
+ def parse(source, mode):
+ return compile(source, '', mode, _ast.PyCF_ONLY_AST)
+
+
+__docformat__ = 'restructuredtext en'
+
+
+class ASTCodeGenerator(object):
+ """General purpose base class for AST transformations.
+
+ Every visitor method can be overridden to return an AST node that has been
+ altered or replaced in some way.
+ """
+ def __init__(self, tree):
+ self.lines_info = []
+ self.line_info = None
+ self.code = ''
+ self.line = None
+ self.last = None
+ self.indent = 0
+ self.blame_stack = []
+ self.visit(tree)
+ if self.line.strip():
+ self.code += self.line + '\n'
+ self.lines_info.append(self.line_info)
+ self.line = None
+ self.line_info = None
+
+ def _change_indent(self, delta):
+ self.indent += delta
+
+ def _new_line(self):
+ if self.line is not None:
+ self.code += self.line + '\n'
+ self.lines_info.append(self.line_info)
+ self.line = ' '*4*self.indent
+ if len(self.blame_stack) == 0:
+ self.line_info = []
+ self.last = None
+ else:
+ self.line_info = [(0, self.blame_stack[-1],)]
+ self.last = self.blame_stack[-1]
+
+ def _write(self, s):
+ if len(s) == 0:
+ return
+ if len(self.blame_stack) == 0:
+ if self.last is not None:
+ self.last = None
+ self.line_info.append((len(self.line), self.last))
+ else:
+ if self.last != self.blame_stack[-1]:
+ self.last = self.blame_stack[-1]
+ self.line_info.append((len(self.line), self.last))
+ self.line += s
+
+ def visit(self, node):
+ if node is None:
+ return None
+ if type(node) is tuple:
+ return tuple([self.visit(n) for n in node])
+ try:
+ self.blame_stack.append((node.lineno, node.col_offset,))
+ info = True
+ except AttributeError:
+ info = False
+ visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None)
+ if visitor is None:
+ raise Exception('Unhandled node type %r' % type(node))
+ ret = visitor(node)
+ if info:
+ self.blame_stack.pop()
+ return ret
+
+ def visit_Module(self, node):
+ for n in node.body:
+ self.visit(n)
+ visit_Interactive = visit_Module
+ visit_Suite = visit_Module
+
+ def visit_Expression(self, node):
+ self._new_line()
+ return self.visit(node.body)
+
+ # arguments = (expr* args, identifier? vararg,
+ # identifier? kwarg, expr* defaults)
+ def visit_arguments(self, node):
+ first = True
+ no_default_count = len(node.args) - len(node.defaults)
+ for i, arg in enumerate(node.args):
+ if not first:
+ self._write(', ')
+ else:
+ first = False
+ self.visit(arg)
+ if i >= no_default_count:
+ self._write('=')
+ self.visit(node.defaults[i - no_default_count])
+ if getattr(node, 'vararg', None):
+ if not first:
+ self._write(', ')
+ else:
+ first = False
+ self._write('*' + node.vararg)
+ if getattr(node, 'kwarg', None):
+ if not first:
+ self._write(', ')
+ else:
+ first = False
+ self._write('**' + node.kwarg)
+
+ # FunctionDef(identifier name, arguments args,
+ # stmt* body, expr* decorator_list)
+ def visit_FunctionDef(self, node):
+ decarators = ()
+ if hasattr(node, 'decorator_list'):
+ decorators = getattr(node, 'decorator_list')
+ else: # different name in earlier Python versions
+ decorators = getattr(node, 'decorators', ())
+ for decorator in decorators:
+ self._new_line()
+ self._write('@')
+ self.visit(decorator)
+ self._new_line()
+ self._write('def ' + node.name + '(')
+ self.visit(node.args)
+ self._write('):')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # ClassDef(identifier name, expr* bases, stmt* body)
+ def visit_ClassDef(self, node):
+ self._new_line()
+ self._write('class ' + node.name)
+ if node.bases:
+ self._write('(')
+ self.visit(node.bases[0])
+ for base in node.bases[1:]:
+ self._write(', ')
+ self.visit(base)
+ self._write(')')
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # Return(expr? value)
+ def visit_Return(self, node):
+ self._new_line()
+ self._write('return')
+ if getattr(node, 'value', None):
+ self._write(' ')
+ self.visit(node.value)
+
+ # Delete(expr* targets)
+ def visit_Delete(self, node):
+ self._new_line()
+ self._write('del ')
+ self.visit(node.targets[0])
+ for target in node.targets[1:]:
+ self._write(', ')
+ self.visit(target)
+
+ # Assign(expr* targets, expr value)
+ def visit_Assign(self, node):
+ self._new_line()
+ for target in node.targets:
+ self.visit(target)
+ self._write(' = ')
+ self.visit(node.value)
+
+ # AugAssign(expr target, operator op, expr value)
+ def visit_AugAssign(self, node):
+ self._new_line()
+ self.visit(node.target)
+ self._write(' ' + self.binary_operators[node.op.__class__] + '= ')
+ self.visit(node.value)
+
+ # Print(expr? dest, expr* values, bool nl)
+ def visit_Print(self, node):
+ self._new_line()
+ self._write('print')
+ if getattr(node, 'dest', None):
+ self._write(' >> ')
+ self.visit(node.dest)
+ if getattr(node, 'values', None):
+ self._write(', ')
+ else:
+ self._write(' ')
+ if getattr(node, 'values', None):
+ self.visit(node.values[0])
+ for value in node.values[1:]:
+ self._write(', ')
+ self.visit(value)
+ if not node.nl:
+ self._write(',')
+
+ # For(expr target, expr iter, stmt* body, stmt* orelse)
+ def visit_For(self, node):
+ self._new_line()
+ self._write('for ')
+ self.visit(node.target)
+ self._write(' in ')
+ self.visit(node.iter)
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+ if getattr(node, 'orelse', None):
+ self._new_line()
+ self._write('else:')
+ self._change_indent(1)
+ for statement in node.orelse:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # While(expr test, stmt* body, stmt* orelse)
+ def visit_While(self, node):
+ self._new_line()
+ self._write('while ')
+ self.visit(node.test)
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+ if getattr(node, 'orelse', None):
+ self._new_line()
+ self._write('else:')
+ self._change_indent(1)
+ for statement in node.orelse:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # If(expr test, stmt* body, stmt* orelse)
+ def visit_If(self, node):
+ self._new_line()
+ self._write('if ')
+ self.visit(node.test)
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+ if getattr(node, 'orelse', None):
+ self._new_line()
+ self._write('else:')
+ self._change_indent(1)
+ for statement in node.orelse:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # With(expr context_expr, expr? optional_vars, stmt* body)
+ def visit_With(self, node):
+ self._new_line()
+ self._write('with ')
+ self.visit(node.context_expr)
+ if getattr(node, 'optional_vars', None):
+ self._write(' as ')
+ self.visit(node.optional_vars)
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+
+
+ # Raise(expr? type, expr? inst, expr? tback)
+ def visit_Raise(self, node):
+ self._new_line()
+ self._write('raise')
+ if not node.type:
+ return
+ self._write(' ')
+ self.visit(node.type)
+ if not node.inst:
+ return
+ self._write(', ')
+ self.visit(node.inst)
+ if not node.tback:
+ return
+ self._write(', ')
+ self.visit(node.tback)
+
+ # TryExcept(stmt* body, excepthandler* handlers, stmt* orelse)
+ def visit_TryExcept(self, node):
+ self._new_line()
+ self._write('try:')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+ if getattr(node, 'handlers', None):
+ for handler in node.handlers:
+ self.visit(handler)
+ self._new_line()
+ if getattr(node, 'orelse', None):
+ self._write('else:')
+ self._change_indent(1)
+ for statement in node.orelse:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # excepthandler = (expr? type, expr? name, stmt* body)
+ def visit_ExceptHandler(self, node):
+ self._new_line()
+ self._write('except')
+ if getattr(node, 'type', None):
+ self._write(' ')
+ self.visit(node.type)
+ if getattr(node, 'name', None):
+ self._write(', ')
+ self.visit(node.name)
+ self._write(':')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+ visit_excepthandler = visit_ExceptHandler
+
+ # TryFinally(stmt* body, stmt* finalbody)
+ def visit_TryFinally(self, node):
+ self._new_line()
+ self._write('try:')
+ self._change_indent(1)
+ for statement in node.body:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ if getattr(node, 'finalbody', None):
+ self._new_line()
+ self._write('finally:')
+ self._change_indent(1)
+ for statement in node.finalbody:
+ self.visit(statement)
+ self._change_indent(-1)
+
+ # Assert(expr test, expr? msg)
+ def visit_Assert(self, node):
+ self._new_line()
+ self._write('assert ')
+ self.visit(node.test)
+ if getattr(node, 'msg', None):
+ self._write(', ')
+ self.visit(node.msg)
+
+ def visit_alias(self, node):
+ self._write(node.name)
+ if getattr(node, 'asname', None):
+ self._write(' as ')
+ self._write(node.asname)
+
+ # Import(alias* names)
+ def visit_Import(self, node):
+ self._new_line()
+ self._write('import ')
+ self.visit(node.names[0])
+ for name in node.names[1:]:
+ self._write(', ')
+ self.visit(name)
+
+ # ImportFrom(identifier module, alias* names, int? level)
+ def visit_ImportFrom(self, node):
+ self._new_line()
+ self._write('from ')
+ if node.level:
+ self._write('.' * node.level)
+ self._write(node.module)
+ self._write(' import ')
+ self.visit(node.names[0])
+ for name in node.names[1:]:
+ self._write(', ')
+ self.visit(name)
+
+ # Exec(expr body, expr? globals, expr? locals)
+ def visit_Exec(self, node):
+ self._new_line()
+ self._write('exec ')
+ self.visit(node.body)
+ if not node.globals:
+ return
+ self._write(', ')
+ self.visit(node.globals)
+ if not node.locals:
+ return
+ self._write(', ')
+ self.visit(node.locals)
+
+ # Global(identifier* names)
+ def visit_Global(self, node):
+ self._new_line()
+ self._write('global ')
+ self.visit(node.names[0])
+ for name in node.names[1:]:
+ self._write(', ')
+ self.visit(name)
+
+ # Expr(expr value)
+ def visit_Expr(self, node):
+ self._new_line()
+ self.visit(node.value)
+
+ # Pass
+ def visit_Pass(self, node):
+ self._new_line()
+ self._write('pass')
+
+ # Break
+ def visit_Break(self, node):
+ self._new_line()
+ self._write('break')
+
+ # Continue
+ def visit_Continue(self, node):
+ self._new_line()
+ self._write('continue')
+
+ ### EXPRESSIONS
+ def with_parens(f):
+ def _f(self, node):
+ self._write('(')
+ f(self, node)
+ self._write(')')
+ return _f
+
+ bool_operators = {_ast.And: 'and', _ast.Or: 'or'}
+
+ # BoolOp(boolop op, expr* values)
+ @with_parens
+ def visit_BoolOp(self, node):
+ joiner = ' ' + self.bool_operators[node.op.__class__] + ' '
+ self.visit(node.values[0])
+ for value in node.values[1:]:
+ self._write(joiner)
+ self.visit(value)
+
+ binary_operators = {
+ _ast.Add: '+',
+ _ast.Sub: '-',
+ _ast.Mult: '*',
+ _ast.Div: '/',
+ _ast.Mod: '%',
+ _ast.Pow: '**',
+ _ast.LShift: '<<',
+ _ast.RShift: '>>',
+ _ast.BitOr: '|',
+ _ast.BitXor: '^',
+ _ast.BitAnd: '&',
+ _ast.FloorDiv: '//'
+ }
+
+ # BinOp(expr left, operator op, expr right)
+ @with_parens
+ def visit_BinOp(self, node):
+ self.visit(node.left)
+ self._write(' ' + self.binary_operators[node.op.__class__] + ' ')
+ self.visit(node.right)
+
+ unary_operators = {
+ _ast.Invert: '~',
+ _ast.Not: 'not',
+ _ast.UAdd: '+',
+ _ast.USub: '-',
+ }
+
+ # UnaryOp(unaryop op, expr operand)
+ def visit_UnaryOp(self, node):
+ self._write(self.unary_operators[node.op.__class__] + ' ')
+ self.visit(node.operand)
+
+ # Lambda(arguments args, expr body)
+ @with_parens
+ def visit_Lambda(self, node):
+ self._write('lambda ')
+ self.visit(node.args)
+ self._write(': ')
+ self.visit(node.body)
+
+ # IfExp(expr test, expr body, expr orelse)
+ @with_parens
+ def visit_IfExp(self, node):
+ self.visit(node.body)
+ self._write(' if ')
+ self.visit(node.test)
+ self._write(' else ')
+ self.visit(node.orelse)
+
+ # Dict(expr* keys, expr* values)
+ def visit_Dict(self, node):
+ self._write('{')
+ for key, value in zip(node.keys, node.values):
+ self.visit(key)
+ self._write(': ')
+ self.visit(value)
+ self._write(', ')
+ self._write('}')
+
+ # ListComp(expr elt, comprehension* generators)
+ def visit_ListComp(self, node):
+ self._write('[')
+ self.visit(node.elt)
+ for generator in node.generators:
+ # comprehension = (expr target, expr iter, expr* ifs)
+ self._write(' for ')
+ self.visit(generator.target)
+ self._write(' in ')
+ self.visit(generator.iter)
+ for ifexpr in generator.ifs:
+ self._write(' if ')
+ self.visit(ifexpr)
+ self._write(']')
+
+ # GeneratorExp(expr elt, comprehension* generators)
+ def visit_GeneratorExp(self, node):
+ self._write('(')
+ self.visit(node.elt)
+ for generator in node.generators:
+ # comprehension = (expr target, expr iter, expr* ifs)
+ self._write(' for ')
+ self.visit(generator.target)
+ self._write(' in ')
+ self.visit(generator.iter)
+ for ifexpr in generator.ifs:
+ self._write(' if ')
+ self.visit(ifexpr)
+ self._write(')')
+
+ # Yield(expr? value)
+ def visit_Yield(self, node):
+ self._write('yield')
+ if getattr(node, 'value', None):
+ self._write(' ')
+ self.visit(node.value)
+
+ comparision_operators = {
+ _ast.Eq: '==',
+ _ast.NotEq: '!=',
+ _ast.Lt: '<',
+ _ast.LtE: '<=',
+ _ast.Gt: '>',
+ _ast.GtE: '>=',
+ _ast.Is: 'is',
+ _ast.IsNot: 'is not',
+ _ast.In: 'in',
+ _ast.NotIn: 'not in',
+ }
+
+ # Compare(expr left, cmpop* ops, expr* comparators)
+ @with_parens
+ def visit_Compare(self, node):
+ self.visit(node.left)
+ for op, comparator in zip(node.ops, node.comparators):
+ self._write(' ' + self.comparision_operators[op.__class__] + ' ')
+ self.visit(comparator)
+
+ # Call(expr func, expr* args, keyword* keywords,
+ # expr? starargs, expr? kwargs)
+ def visit_Call(self, node):
+ self.visit(node.func)
+ self._write('(')
+ first = True
+ for arg in node.args:
+ if not first:
+ self._write(', ')
+ first = False
+ self.visit(arg)
+
+ for keyword in node.keywords:
+ if not first:
+ self._write(', ')
+ first = False
+ # keyword = (identifier arg, expr value)
+ self._write(keyword.arg)
+ self._write('=')
+ self.visit(keyword.value)
+ if getattr(node, 'starargs', None):
+ if not first:
+ self._write(', ')
+ first = False
+ self._write('*')
+ self.visit(node.starargs)
+
+ if getattr(node, 'kwargs', None):
+ if not first:
+ self._write(', ')
+ first = False
+ self._write('**')
+ self.visit(node.kwargs)
+ self._write(')')
+
+ # Repr(expr value)
+ def visit_Repr(self, node):
+ self._write('`')
+ self.visit(node.value)
+ self._write('`')
+
+ # Num(object n)
+ def visit_Num(self, node):
+ self._write(repr(node.n))
+
+ # Str(string s)
+ def visit_Str(self, node):
+ self._write(repr(node.s))
+
+ # Attribute(expr value, identifier attr, expr_context ctx)
+ def visit_Attribute(self, node):
+ self.visit(node.value)
+ self._write('.')
+ self._write(node.attr)
+
+ # Subscript(expr value, slice slice, expr_context ctx)
+ def visit_Subscript(self, node):
+ self.visit(node.value)
+ self._write('[')
+ def _process_slice(node):
+ if isinstance(node, _ast.Ellipsis):
+ self._write('...')
+ elif isinstance(node, _ast.Slice):
+ if getattr(node, 'lower', 'None'):
+ self.visit(node.lower)
+ self._write(':')
+ if getattr(node, 'upper', None):
+ self.visit(node.upper)
+ if getattr(node, 'step', None):
+ self._write(':')
+ self.visit(node.step)
+ elif isinstance(node, _ast.Index):
+ self.visit(node.value)
+ elif isinstance(node, _ast.ExtSlice):
+ self.visit(node.dims[0])
+ for dim in node.dims[1:]:
+ self._write(', ')
+ self.visit(dim)
+ else:
+ raise NotImplemented('Slice type not implemented')
+ _process_slice(node.slice)
+ self._write(']')
+
+ # Name(identifier id, expr_context ctx)
+ def visit_Name(self, node):
+ self._write(node.id)
+
+ # List(expr* elts, expr_context ctx)
+ def visit_List(self, node):
+ self._write('[')
+ for elt in node.elts:
+ self.visit(elt)
+ self._write(', ')
+ self._write(']')
+
+ # Tuple(expr *elts, expr_context ctx)
+ def visit_Tuple(self, node):
+ self._write('(')
+ for elt in node.elts:
+ self.visit(elt)
+ self._write(', ')
+ self._write(')')
+
+
+class ASTTransformer(object):
+ """General purpose base class for AST transformations.
+
+ Every visitor method can be overridden to return an AST node that has been
+ altered or replaced in some way.
+ """
+
+ def visit(self, node):
+ if node is None:
+ return None
+ if type(node) is tuple:
+ return tuple([self.visit(n) for n in node])
+ visitor = getattr(self, 'visit_%s' % node.__class__.__name__, None)
+ if visitor is None:
+ return node
+ return visitor(node)
+
+ def _clone(self, node):
+ clone = node.__class__()
+ for name in getattr(clone, '_attributes', ()):
+ try:
+ setattr(clone, 'name', getattr(node, name))
+ except AttributeError:
+ pass
+ for name in clone._fields:
+ try:
+ value = getattr(node, name)
+ except AttributeError:
+ pass
+ else:
+ if value is None:
+ pass
+ elif isinstance(value, list):
+ value = [self.visit(x) for x in value]
+ elif isinstance(value, tuple):
+ value = tuple(self.visit(x) for x in value)
+ else:
+ value = self.visit(value)
+ setattr(clone, name, value)
+ return clone
+
+ visit_Module = _clone
+ visit_Interactive = _clone
+ visit_Expression = _clone
+ visit_Suite = _clone
+
+ visit_FunctionDef = _clone
+ visit_ClassDef = _clone
+ visit_Return = _clone
+ visit_Delete = _clone
+ visit_Assign = _clone
+ visit_AugAssign = _clone
+ visit_Print = _clone
+ visit_For = _clone
+ visit_While = _clone
+ visit_If = _clone
+ visit_With = _clone
+ visit_Raise = _clone
+ visit_TryExcept = _clone
+ visit_TryFinally = _clone
+ visit_Assert = _clone
+ visit_ExceptHandler = _clone
+
+ visit_Import = _clone
+ visit_ImportFrom = _clone
+ visit_Exec = _clone
+ visit_Global = _clone
+ visit_Expr = _clone
+ # Pass, Break, Continue don't need to be copied
+
+ visit_BoolOp = _clone
+ visit_BinOp = _clone
+ visit_UnaryOp = _clone
+ visit_Lambda = _clone
+ visit_IfExp = _clone
+ visit_Dict = _clone
+ visit_ListComp = _clone
+ visit_GeneratorExp = _clone
+ visit_Yield = _clone
+ visit_Compare = _clone
+ visit_Call = _clone
+ visit_Repr = _clone
+ # Num, Str don't need to be copied
+
+ visit_Attribute = _clone
+ visit_Subscript = _clone
+ visit_Name = _clone
+ visit_List = _clone
+ visit_Tuple = _clone
+
+ visit_comprehension = _clone
+ visit_excepthandler = _clone
+ visit_arguments = _clone
+ visit_keyword = _clone
+ visit_alias = _clone
+
+ visit_Slice = _clone
+ visit_ExtSlice = _clone
+ visit_Index = _clone
+
+ del _clone
diff --git a/websdk/genshi/template/base.py b/websdk/genshi/template/base.py
new file mode 100644
index 0000000..202faae
--- /dev/null
+++ b/websdk/genshi/template/base.py
@@ -0,0 +1,634 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2010 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Basic templating functionality."""
+
+from collections import deque
+import os
+from StringIO import StringIO
+import sys
+
+from genshi.core import Attrs, Stream, StreamEventKind, START, TEXT, _ensure
+from genshi.input import ParseError
+
+__all__ = ['Context', 'DirectiveFactory', 'Template', 'TemplateError',
+ 'TemplateRuntimeError', 'TemplateSyntaxError', 'BadDirectiveError']
+__docformat__ = 'restructuredtext en'
+
+
+class TemplateError(Exception):
+ """Base exception class for errors related to template processing."""
+
+ def __init__(self, message, filename=None, lineno=-1, offset=-1):
+ """Create the exception.
+
+ :param message: the error message
+ :param filename: the filename of the template
+ :param lineno: the number of line in the template at which the error
+ occurred
+ :param offset: the column number at which the error occurred
+ """
+ if filename is None:
+ filename = '<string>'
+ self.msg = message #: the error message string
+ if filename != '<string>' or lineno >= 0:
+ message = '%s (%s, line %d)' % (self.msg, filename, lineno)
+ Exception.__init__(self, message)
+ self.filename = filename #: the name of the template file
+ self.lineno = lineno #: the number of the line containing the error
+ self.offset = offset #: the offset on the line
+
+
+class TemplateSyntaxError(TemplateError):
+ """Exception raised when an expression in a template causes a Python syntax
+ error, or the template is not well-formed.
+ """
+
+ def __init__(self, message, filename=None, lineno=-1, offset=-1):
+ """Create the exception
+
+ :param message: the error message
+ :param filename: the filename of the template
+ :param lineno: the number of line in the template at which the error
+ occurred
+ :param offset: the column number at which the error occurred
+ """
+ if isinstance(message, SyntaxError) and message.lineno is not None:
+ message = str(message).replace(' (line %d)' % message.lineno, '')
+ TemplateError.__init__(self, message, filename, lineno)
+
+
+class BadDirectiveError(TemplateSyntaxError):
+ """Exception raised when an unknown directive is encountered when parsing
+ a template.
+
+ An unknown directive is any attribute using the namespace for directives,
+ with a local name that doesn't match any registered directive.
+ """
+
+ def __init__(self, name, filename=None, lineno=-1):
+ """Create the exception
+
+ :param name: the name of the directive
+ :param filename: the filename of the template
+ :param lineno: the number of line in the template at which the error
+ occurred
+ """
+ TemplateSyntaxError.__init__(self, 'bad directive "%s"' % name,
+ filename, lineno)
+
+
+class TemplateRuntimeError(TemplateError):
+ """Exception raised when an the evaluation of a Python expression in a
+ template causes an error.
+ """
+
+
+class Context(object):
+ """Container for template input data.
+
+ A context provides a stack of scopes (represented by dictionaries).
+
+ Template directives such as loops can push a new scope on the stack with
+ data that should only be available inside the loop. When the loop
+ terminates, that scope can get popped off the stack again.
+
+ >>> ctxt = Context(one='foo', other=1)
+ >>> ctxt.get('one')
+ 'foo'
+ >>> ctxt.get('other')
+ 1
+ >>> ctxt.push(dict(one='frost'))
+ >>> ctxt.get('one')
+ 'frost'
+ >>> ctxt.get('other')
+ 1
+ >>> ctxt.pop()
+ {'one': 'frost'}
+ >>> ctxt.get('one')
+ 'foo'
+ """
+
+ def __init__(self, **data):
+ """Initialize the template context with the given keyword arguments as
+ data.
+ """
+ self.frames = deque([data])
+ self.pop = self.frames.popleft
+ self.push = self.frames.appendleft
+ self._match_templates = []
+ self._choice_stack = []
+
+ # Helper functions for use in expressions
+ def defined(name):
+ """Return whether a variable with the specified name exists in the
+ expression scope."""
+ return name in self
+ def value_of(name, default=None):
+ """If a variable of the specified name is defined, return its value.
+ Otherwise, return the provided default value, or ``None``."""
+ return self.get(name, default)
+ data.setdefault('defined', defined)
+ data.setdefault('value_of', value_of)
+
+ def __repr__(self):
+ return repr(list(self.frames))
+
+ def __contains__(self, key):
+ """Return whether a variable exists in any of the scopes.
+
+ :param key: the name of the variable
+ """
+ return self._find(key)[1] is not None
+ has_key = __contains__
+
+ def __delitem__(self, key):
+ """Remove a variable from all scopes.
+
+ :param key: the name of the variable
+ """
+ for frame in self.frames:
+ if key in frame:
+ del frame[key]
+
+ def __getitem__(self, key):
+ """Get a variables's value, starting at the current scope and going
+ upward.
+
+ :param key: the name of the variable
+ :return: the variable value
+ :raises KeyError: if the requested variable wasn't found in any scope
+ """
+ value, frame = self._find(key)
+ if frame is None:
+ raise KeyError(key)
+ return value
+
+ def __len__(self):
+ """Return the number of distinctly named variables in the context.
+
+ :return: the number of variables in the context
+ """
+ return len(self.items())
+
+ def __setitem__(self, key, value):
+ """Set a variable in the current scope.
+
+ :param key: the name of the variable
+ :param value: the variable value
+ """
+ self.frames[0][key] = value
+
+ def _find(self, key, default=None):
+ """Retrieve a given variable's value and the frame it was found in.
+
+ Intended primarily for internal use by directives.
+
+ :param key: the name of the variable
+ :param default: the default value to return when the variable is not
+ found
+ """
+ for frame in self.frames:
+ if key in frame:
+ return frame[key], frame
+ return default, None
+
+ def get(self, key, default=None):
+ """Get a variable's value, starting at the current scope and going
+ upward.
+
+ :param key: the name of the variable
+ :param default: the default value to return when the variable is not
+ found
+ """
+ for frame in self.frames:
+ if key in frame:
+ return frame[key]
+ return default
+
+ def keys(self):
+ """Return the name of all variables in the context.
+
+ :return: a list of variable names
+ """
+ keys = []
+ for frame in self.frames:
+ keys += [key for key in frame if key not in keys]
+ return keys
+
+ def items(self):
+ """Return a list of ``(name, value)`` tuples for all variables in the
+ context.
+
+ :return: a list of variables
+ """
+ return [(key, self.get(key)) for key in self.keys()]
+
+ def update(self, mapping):
+ """Update the context from the mapping provided."""
+ self.frames[0].update(mapping)
+
+ def push(self, data):
+ """Push a new scope on the stack.
+
+ :param data: the data dictionary to push on the context stack.
+ """
+
+ def pop(self):
+ """Pop the top-most scope from the stack."""
+
+
+def _apply_directives(stream, directives, ctxt, vars):
+ """Apply the given directives to the stream.
+
+ :param stream: the stream the directives should be applied to
+ :param directives: the list of directives to apply
+ :param ctxt: the `Context`
+ :param vars: additional variables that should be available when Python
+ code is executed
+ :return: the stream with the given directives applied
+ """
+ if directives:
+ stream = directives[0](iter(stream), directives[1:], ctxt, **vars)
+ return stream
+
+
+def _eval_expr(expr, ctxt, vars=None):
+ """Evaluate the given `Expression` object.
+
+ :param expr: the expression to evaluate
+ :param ctxt: the `Context`
+ :param vars: additional variables that should be available to the
+ expression
+ :return: the result of the evaluation
+ """
+ if vars:
+ ctxt.push(vars)
+ retval = expr.evaluate(ctxt)
+ if vars:
+ ctxt.pop()
+ return retval
+
+
+def _exec_suite(suite, ctxt, vars=None):
+ """Execute the given `Suite` object.
+
+ :param suite: the code suite to execute
+ :param ctxt: the `Context`
+ :param vars: additional variables that should be available to the
+ code
+ """
+ if vars:
+ ctxt.push(vars)
+ ctxt.push({})
+ suite.execute(ctxt)
+ if vars:
+ top = ctxt.pop()
+ ctxt.pop()
+ ctxt.frames[0].update(top)
+
+
+class DirectiveFactoryMeta(type):
+ """Meta class for directive factories."""
+
+ def __new__(cls, name, bases, d):
+ if 'directives' in d:
+ d['_dir_by_name'] = dict(d['directives'])
+ d['_dir_order'] = [directive[1] for directive in d['directives']]
+
+ return type.__new__(cls, name, bases, d)
+
+
+class DirectiveFactory(object):
+ """Base for classes that provide a set of template directives.
+
+ :since: version 0.6
+ """
+ __metaclass__ = DirectiveFactoryMeta
+
+ directives = []
+ """A list of ``(name, cls)`` tuples that define the set of directives
+ provided by this factory.
+ """
+
+ def get_directive(self, name):
+ """Return the directive class for the given name.
+
+ :param name: the directive name as used in the template
+ :return: the directive class
+ :see: `Directive`
+ """
+ return self._dir_by_name.get(name)
+
+ def get_directive_index(self, dir_cls):
+ """Return a key for the given directive class that should be used to
+ sort it among other directives on the same `SUB` event.
+
+ The default implementation simply returns the index of the directive in
+ the `directives` list.
+
+ :param dir_cls: the directive class
+ :return: the sort key
+ """
+ if dir_cls in self._dir_order:
+ return self._dir_order.index(dir_cls)
+ return len(self._dir_order)
+
+
+class Template(DirectiveFactory):
+ """Abstract template base class.
+
+ This class implements most of the template processing model, but does not
+ specify the syntax of templates.
+ """
+
+ EXEC = StreamEventKind('EXEC')
+ """Stream event kind representing a Python code suite to execute."""
+
+ EXPR = StreamEventKind('EXPR')
+ """Stream event kind representing a Python expression."""
+
+ INCLUDE = StreamEventKind('INCLUDE')
+ """Stream event kind representing the inclusion of another template."""
+
+ SUB = StreamEventKind('SUB')
+ """Stream event kind representing a nested stream to which one or more
+ directives should be applied.
+ """
+
+ serializer = None
+ _number_conv = unicode # function used to convert numbers to event data
+
+ def __init__(self, source, filepath=None, filename=None, loader=None,
+ encoding=None, lookup='strict', allow_exec=True):
+ """Initialize a template from either a string, a file-like object, or
+ an already parsed markup stream.
+
+ :param source: a string, file-like object, or markup stream to read the
+ template from
+ :param filepath: the absolute path to the template file
+ :param filename: the path to the template file relative to the search
+ path
+ :param loader: the `TemplateLoader` to use for loading included
+ templates
+ :param encoding: the encoding of the `source`
+ :param lookup: the variable lookup mechanism; either "strict" (the
+ default), "lenient", or a custom lookup class
+ :param allow_exec: whether Python code blocks in templates should be
+ allowed
+
+ :note: Changed in 0.5: Added the `allow_exec` argument
+ """
+ self.filepath = filepath or filename
+ self.filename = filename
+ self.loader = loader
+ self.lookup = lookup
+ self.allow_exec = allow_exec
+ self._init_filters()
+ self._init_loader()
+ self._prepared = False
+
+ if isinstance(source, basestring):
+ source = StringIO(source)
+ else:
+ source = source
+ try:
+ self._stream = self._parse(source, encoding)
+ except ParseError, e:
+ raise TemplateSyntaxError(e.msg, self.filepath, e.lineno, e.offset)
+
+ def __getstate__(self):
+ state = self.__dict__.copy()
+ state['filters'] = []
+ return state
+
+ def __setstate__(self, state):
+ self.__dict__ = state
+ self._init_filters()
+
+ def __repr__(self):
+ return '<%s "%s">' % (type(self).__name__, self.filename)
+
+ def _init_filters(self):
+ self.filters = [self._flatten, self._include]
+
+ def _init_loader(self):
+ if self.loader is None:
+ from genshi.template.loader import TemplateLoader
+ if self.filename:
+ if self.filepath != self.filename:
+ basedir = os.path.normpath(self.filepath)[:-len(
+ os.path.normpath(self.filename))
+ ]
+ else:
+ basedir = os.path.dirname(self.filename)
+ else:
+ basedir = '.'
+ self.loader = TemplateLoader([os.path.abspath(basedir)])
+
+ @property
+ def stream(self):
+ if not self._prepared:
+ self._stream = list(self._prepare(self._stream))
+ self._prepared = True
+ return self._stream
+
+ def _parse(self, source, encoding):
+ """Parse the template.
+
+ The parsing stage parses the template and constructs a list of
+ directives that will be executed in the render stage. The input is
+ split up into literal output (text that does not depend on the context
+ data) and directives or expressions.
+
+ :param source: a file-like object containing the XML source of the
+ template, or an XML event stream
+ :param encoding: the encoding of the `source`
+ """
+ raise NotImplementedError
+
+ def _prepare(self, stream):
+ """Call the `attach` method of every directive found in the template.
+
+ :param stream: the event stream of the template
+ """
+ from genshi.template.loader import TemplateNotFound
+
+ for kind, data, pos in stream:
+ if kind is SUB:
+ directives = []
+ substream = data[1]
+ for _, cls, value, namespaces, pos in sorted(data[0]):
+ directive, substream = cls.attach(self, substream, value,
+ namespaces, pos)
+ if directive:
+ directives.append(directive)
+ substream = self._prepare(substream)
+ if directives:
+ yield kind, (directives, list(substream)), pos
+ else:
+ for event in substream:
+ yield event
+ else:
+ if kind is INCLUDE:
+ href, cls, fallback = data
+ if isinstance(href, basestring) and \
+ not getattr(self.loader, 'auto_reload', True):
+ # If the path to the included template is static, and
+ # auto-reloading is disabled on the template loader,
+ # the template is inlined into the stream
+ try:
+ tmpl = self.loader.load(href, relative_to=pos[0],
+ cls=cls or self.__class__)
+ for event in tmpl.stream:
+ yield event
+ except TemplateNotFound:
+ if fallback is None:
+ raise
+ for event in self._prepare(fallback):
+ yield event
+ continue
+ elif fallback:
+ # Otherwise the include is performed at run time
+ data = href, cls, list(self._prepare(fallback))
+
+ yield kind, data, pos
+
+ def generate(self, *args, **kwargs):
+ """Apply the template to the given context data.
+
+ Any keyword arguments are made available to the template as context
+ data.
+
+ Only one positional argument is accepted: if it is provided, it must be
+ an instance of the `Context` class, and keyword arguments are ignored.
+ This calling style is used for internal processing.
+
+ :return: a markup event stream representing the result of applying
+ the template to the context data.
+ """
+ vars = {}
+ if args:
+ assert len(args) == 1
+ ctxt = args[0]
+ if ctxt is None:
+ ctxt = Context(**kwargs)
+ else:
+ vars = kwargs
+ assert isinstance(ctxt, Context)
+ else:
+ ctxt = Context(**kwargs)
+
+ stream = self.stream
+ for filter_ in self.filters:
+ stream = filter_(iter(stream), ctxt, **vars)
+ return Stream(stream, self.serializer)
+
+ def _flatten(self, stream, ctxt, **vars):
+ number_conv = self._number_conv
+ stack = []
+ push = stack.append
+ pop = stack.pop
+ stream = iter(stream)
+
+ while 1:
+ for kind, data, pos in stream:
+
+ if kind is START and data[1]:
+ # Attributes may still contain expressions in start tags at
+ # this point, so do some evaluation
+ tag, attrs = data
+ new_attrs = []
+ for name, value in attrs:
+ if type(value) is list: # this is an interpolated string
+ values = [event[1]
+ for event in self._flatten(value, ctxt, **vars)
+ if event[0] is TEXT and event[1] is not None
+ ]
+ if not values:
+ continue
+ value = ''.join(values)
+ new_attrs.append((name, value))
+ yield kind, (tag, Attrs(new_attrs)), pos
+
+ elif kind is EXPR:
+ result = _eval_expr(data, ctxt, vars)
+ if result is not None:
+ # First check for a string, otherwise the iterable test
+ # below succeeds, and the string will be chopped up into
+ # individual characters
+ if isinstance(result, basestring):
+ yield TEXT, result, pos
+ elif isinstance(result, (int, float, long)):
+ yield TEXT, number_conv(result), pos
+ elif hasattr(result, '__iter__'):
+ push(stream)
+ stream = _ensure(result)
+ break
+ else:
+ yield TEXT, unicode(result), pos
+
+ elif kind is SUB:
+ # This event is a list of directives and a list of nested
+ # events to which those directives should be applied
+ push(stream)
+ stream = _apply_directives(data[1], data[0], ctxt, vars)
+ break
+
+ elif kind is EXEC:
+ _exec_suite(data, ctxt, vars)
+
+ else:
+ yield kind, data, pos
+
+ else:
+ if not stack:
+ break
+ stream = pop()
+
+ def _include(self, stream, ctxt, **vars):
+ """Internal stream filter that performs inclusion of external
+ template files.
+ """
+ from genshi.template.loader import TemplateNotFound
+
+ for event in stream:
+ if event[0] is INCLUDE:
+ href, cls, fallback = event[1]
+ if not isinstance(href, basestring):
+ parts = []
+ for subkind, subdata, subpos in self._flatten(href, ctxt,
+ **vars):
+ if subkind is TEXT:
+ parts.append(subdata)
+ href = ''.join([x for x in parts if x is not None])
+ try:
+ tmpl = self.loader.load(href, relative_to=event[2][0],
+ cls=cls or self.__class__)
+ for event in tmpl.generate(ctxt, **vars):
+ yield event
+ except TemplateNotFound:
+ if fallback is None:
+ raise
+ for filter_ in self.filters:
+ fallback = filter_(iter(fallback), ctxt, **vars)
+ for event in fallback:
+ yield event
+ else:
+ yield event
+
+
+EXEC = Template.EXEC
+EXPR = Template.EXPR
+INCLUDE = Template.INCLUDE
+SUB = Template.SUB
diff --git a/websdk/genshi/template/directives.py b/websdk/genshi/template/directives.py
new file mode 100644
index 0000000..e2c9424
--- /dev/null
+++ b/websdk/genshi/template/directives.py
@@ -0,0 +1,725 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Implementation of the various template directives."""
+
+from genshi.core import QName, Stream
+from genshi.path import Path
+from genshi.template.base import TemplateRuntimeError, TemplateSyntaxError, \
+ EXPR, _apply_directives, _eval_expr
+from genshi.template.eval import Expression, ExpressionASTTransformer, \
+ _ast, _parse
+
+__all__ = ['AttrsDirective', 'ChooseDirective', 'ContentDirective',
+ 'DefDirective', 'ForDirective', 'IfDirective', 'MatchDirective',
+ 'OtherwiseDirective', 'ReplaceDirective', 'StripDirective',
+ 'WhenDirective', 'WithDirective']
+__docformat__ = 'restructuredtext en'
+
+
+class DirectiveMeta(type):
+ """Meta class for template directives."""
+
+ def __new__(cls, name, bases, d):
+ d['tagname'] = name.lower().replace('directive', '')
+ return type.__new__(cls, name, bases, d)
+
+
+class Directive(object):
+ """Abstract base class for template directives.
+
+ A directive is basically a callable that takes three positional arguments:
+ ``ctxt`` is the template data context, ``stream`` is an iterable over the
+ events that the directive applies to, and ``directives`` is is a list of
+ other directives on the same stream that need to be applied.
+
+ Directives can be "anonymous" or "registered". Registered directives can be
+ applied by the template author using an XML attribute with the
+ corresponding name in the template. Such directives should be subclasses of
+ this base class that can be instantiated with the value of the directive
+ attribute as parameter.
+
+ Anonymous directives are simply functions conforming to the protocol
+ described above, and can only be applied programmatically (for example by
+ template filters).
+ """
+ __metaclass__ = DirectiveMeta
+ __slots__ = ['expr']
+
+ def __init__(self, value, template=None, namespaces=None, lineno=-1,
+ offset=-1):
+ self.expr = self._parse_expr(value, template, lineno, offset)
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ """Called after the template stream has been completely parsed.
+
+ :param template: the `Template` object
+ :param stream: the event stream associated with the directive
+ :param value: the argument value for the directive; if the directive was
+ specified as an element, this will be an `Attrs` instance
+ with all specified attributes, otherwise it will be a
+ `unicode` object with just the attribute value
+ :param namespaces: a mapping of namespace URIs to prefixes
+ :param pos: a ``(filename, lineno, offset)`` tuple describing the
+ location where the directive was found in the source
+
+ This class method should return a ``(directive, stream)`` tuple. If
+ ``directive`` is not ``None``, it should be an instance of the `Directive`
+ class, and gets added to the list of directives applied to the substream
+ at runtime. `stream` is an event stream that replaces the original
+ stream associated with the directive.
+ """
+ return cls(value, template, namespaces, *pos[1:]), stream
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ """Apply the directive to the given stream.
+
+ :param stream: the event stream
+ :param directives: a list of the remaining directives that should
+ process the stream
+ :param ctxt: the context data
+ :param vars: additional variables that should be made available when
+ Python code is executed
+ """
+ raise NotImplementedError
+
+ def __repr__(self):
+ expr = ''
+ if getattr(self, 'expr', None) is not None:
+ expr = ' "%s"' % self.expr.source
+ return '<%s%s>' % (type(self).__name__, expr)
+
+ @classmethod
+ def _parse_expr(cls, expr, template, lineno=-1, offset=-1):
+ """Parses the given expression, raising a useful error message when a
+ syntax error is encountered.
+ """
+ try:
+ return expr and Expression(expr, template.filepath, lineno,
+ lookup=template.lookup) or None
+ except SyntaxError, err:
+ err.msg += ' in expression "%s" of "%s" directive' % (expr,
+ cls.tagname)
+ raise TemplateSyntaxError(err, template.filepath, lineno,
+ offset + (err.offset or 0))
+
+
+def _assignment(ast):
+ """Takes the AST representation of an assignment, and returns a
+ function that applies the assignment of a given value to a dictionary.
+ """
+ def _names(node):
+ if isinstance(node, _ast.Tuple):
+ return tuple([_names(child) for child in node.elts])
+ elif isinstance(node, _ast.Name):
+ return node.id
+ def _assign(data, value, names=_names(ast)):
+ if type(names) is tuple:
+ for idx in range(len(names)):
+ _assign(data, value[idx], names[idx])
+ else:
+ data[names] = value
+ return _assign
+
+
+class AttrsDirective(Directive):
+ """Implementation of the ``py:attrs`` template directive.
+
+ The value of the ``py:attrs`` attribute should be a dictionary or a sequence
+ of ``(name, value)`` tuples. The items in that dictionary or sequence are
+ added as attributes to the element:
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('''<ul xmlns:py="http://genshi.edgewall.org/">
+ ... <li py:attrs="foo">Bar</li>
+ ... </ul>''')
+ >>> print(tmpl.generate(foo={'class': 'collapse'}))
+ <ul>
+ <li class="collapse">Bar</li>
+ </ul>
+ >>> print(tmpl.generate(foo=[('class', 'collapse')]))
+ <ul>
+ <li class="collapse">Bar</li>
+ </ul>
+
+ If the value evaluates to ``None`` (or any other non-truth value), no
+ attributes are added:
+
+ >>> print(tmpl.generate(foo=None))
+ <ul>
+ <li>Bar</li>
+ </ul>
+ """
+ __slots__ = []
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ def _generate():
+ kind, (tag, attrib), pos = stream.next()
+ attrs = _eval_expr(self.expr, ctxt, vars)
+ if attrs:
+ if isinstance(attrs, Stream):
+ try:
+ attrs = iter(attrs).next()
+ except StopIteration:
+ attrs = []
+ elif not isinstance(attrs, list): # assume it's a dict
+ attrs = attrs.items()
+ attrib -= [name for name, val in attrs if val is None]
+ attrib |= [(QName(name), unicode(val).strip()) for name, val
+ in attrs if val is not None]
+ yield kind, (tag, attrib), pos
+ for event in stream:
+ yield event
+
+ return _apply_directives(_generate(), directives, ctxt, vars)
+
+
+class ContentDirective(Directive):
+ """Implementation of the ``py:content`` template directive.
+
+ This directive replaces the content of the element with the result of
+ evaluating the value of the ``py:content`` attribute:
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('''<ul xmlns:py="http://genshi.edgewall.org/">
+ ... <li py:content="bar">Hello</li>
+ ... </ul>''')
+ >>> print(tmpl.generate(bar='Bye'))
+ <ul>
+ <li>Bye</li>
+ </ul>
+ """
+ __slots__ = []
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ raise TemplateSyntaxError('The content directive can not be used '
+ 'as an element', template.filepath,
+ *pos[1:])
+ expr = cls._parse_expr(value, template, *pos[1:])
+ return None, [stream[0], (EXPR, expr, pos), stream[-1]]
+
+
+class DefDirective(Directive):
+ """Implementation of the ``py:def`` template directive.
+
+ This directive can be used to create "Named Template Functions", which
+ are template snippets that are not actually output during normal
+ processing, but rather can be expanded from expressions in other places
+ in the template.
+
+ A named template function can be used just like a normal Python function
+ from template expressions:
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/">
+ ... <p py:def="echo(greeting, name='world')" class="message">
+ ... ${greeting}, ${name}!
+ ... </p>
+ ... ${echo('Hi', name='you')}
+ ... </div>''')
+ >>> print(tmpl.generate(bar='Bye'))
+ <div>
+ <p class="message">
+ Hi, you!
+ </p>
+ </div>
+
+ If a function does not require parameters, the parenthesis can be omitted
+ in the definition:
+
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/">
+ ... <p py:def="helloworld" class="message">
+ ... Hello, world!
+ ... </p>
+ ... ${helloworld()}
+ ... </div>''')
+ >>> print(tmpl.generate(bar='Bye'))
+ <div>
+ <p class="message">
+ Hello, world!
+ </p>
+ </div>
+ """
+ __slots__ = ['name', 'args', 'star_args', 'dstar_args', 'defaults']
+
+ def __init__(self, args, template, namespaces=None, lineno=-1, offset=-1):
+ Directive.__init__(self, None, template, namespaces, lineno, offset)
+ ast = _parse(args).body
+ self.args = []
+ self.star_args = None
+ self.dstar_args = None
+ self.defaults = {}
+ if isinstance(ast, _ast.Call):
+ self.name = ast.func.id
+ for arg in ast.args:
+ # only names
+ self.args.append(arg.id)
+ for kwd in ast.keywords:
+ self.args.append(kwd.arg)
+ exp = Expression(kwd.value, template.filepath,
+ lineno, lookup=template.lookup)
+ self.defaults[kwd.arg] = exp
+ if getattr(ast, 'starargs', None):
+ self.star_args = ast.starargs.id
+ if getattr(ast, 'kwargs', None):
+ self.dstar_args = ast.kwargs.id
+ else:
+ self.name = ast.id
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ value = value.get('function')
+ return super(DefDirective, cls).attach(template, stream, value,
+ namespaces, pos)
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ stream = list(stream)
+
+ def function(*args, **kwargs):
+ scope = {}
+ args = list(args) # make mutable
+ for name in self.args:
+ if args:
+ scope[name] = args.pop(0)
+ else:
+ if name in kwargs:
+ val = kwargs.pop(name)
+ else:
+ val = _eval_expr(self.defaults.get(name), ctxt, vars)
+ scope[name] = val
+ if not self.star_args is None:
+ scope[self.star_args] = args
+ if not self.dstar_args is None:
+ scope[self.dstar_args] = kwargs
+ ctxt.push(scope)
+ for event in _apply_directives(stream, directives, ctxt, vars):
+ yield event
+ ctxt.pop()
+ function.__name__ = self.name
+
+ # Store the function reference in the bottom context frame so that it
+ # doesn't get popped off before processing the template has finished
+ # FIXME: this makes context data mutable as a side-effect
+ ctxt.frames[-1][self.name] = function
+
+ return []
+
+ def __repr__(self):
+ return '<%s "%s">' % (type(self).__name__, self.name)
+
+
+class ForDirective(Directive):
+ """Implementation of the ``py:for`` template directive for repeating an
+ element based on an iterable in the context data.
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('''<ul xmlns:py="http://genshi.edgewall.org/">
+ ... <li py:for="item in items">${item}</li>
+ ... </ul>''')
+ >>> print(tmpl.generate(items=[1, 2, 3]))
+ <ul>
+ <li>1</li><li>2</li><li>3</li>
+ </ul>
+ """
+ __slots__ = ['assign', 'filename']
+
+ def __init__(self, value, template, namespaces=None, lineno=-1, offset=-1):
+ if ' in ' not in value:
+ raise TemplateSyntaxError('"in" keyword missing in "for" directive',
+ template.filepath, lineno, offset)
+ assign, value = value.split(' in ', 1)
+ ast = _parse(assign, 'exec')
+ value = 'iter(%s)' % value.strip()
+ self.assign = _assignment(ast.body[0].value)
+ self.filename = template.filepath
+ Directive.__init__(self, value, template, namespaces, lineno, offset)
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ value = value.get('each')
+ return super(ForDirective, cls).attach(template, stream, value,
+ namespaces, pos)
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ iterable = _eval_expr(self.expr, ctxt, vars)
+ if iterable is None:
+ return
+
+ assign = self.assign
+ scope = {}
+ stream = list(stream)
+ for item in iterable:
+ assign(scope, item)
+ ctxt.push(scope)
+ for event in _apply_directives(stream, directives, ctxt, vars):
+ yield event
+ ctxt.pop()
+
+ def __repr__(self):
+ return '<%s>' % type(self).__name__
+
+
+class IfDirective(Directive):
+ """Implementation of the ``py:if`` template directive for conditionally
+ excluding elements from being output.
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/">
+ ... <b py:if="foo">${bar}</b>
+ ... </div>''')
+ >>> print(tmpl.generate(foo=True, bar='Hello'))
+ <div>
+ <b>Hello</b>
+ </div>
+ """
+ __slots__ = []
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ value = value.get('test')
+ return super(IfDirective, cls).attach(template, stream, value,
+ namespaces, pos)
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ value = _eval_expr(self.expr, ctxt, vars)
+ if value:
+ return _apply_directives(stream, directives, ctxt, vars)
+ return []
+
+
+class MatchDirective(Directive):
+ """Implementation of the ``py:match`` template directive.
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/">
+ ... <span py:match="greeting">
+ ... Hello ${select('@name')}
+ ... </span>
+ ... <greeting name="Dude" />
+ ... </div>''')
+ >>> print(tmpl.generate())
+ <div>
+ <span>
+ Hello Dude
+ </span>
+ </div>
+ """
+ __slots__ = ['path', 'namespaces', 'hints']
+
+ def __init__(self, value, template, hints=None, namespaces=None,
+ lineno=-1, offset=-1):
+ Directive.__init__(self, None, template, namespaces, lineno, offset)
+ self.path = Path(value, template.filepath, lineno)
+ self.namespaces = namespaces or {}
+ self.hints = hints or ()
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ hints = []
+ if type(value) is dict:
+ if value.get('buffer', '').lower() == 'false':
+ hints.append('not_buffered')
+ if value.get('once', '').lower() == 'true':
+ hints.append('match_once')
+ if value.get('recursive', '').lower() == 'false':
+ hints.append('not_recursive')
+ value = value.get('path')
+ return cls(value, template, frozenset(hints), namespaces, *pos[1:]), \
+ stream
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ ctxt._match_templates.append((self.path.test(ignore_context=True),
+ self.path, list(stream), self.hints,
+ self.namespaces, directives))
+ return []
+
+ def __repr__(self):
+ return '<%s "%s">' % (type(self).__name__, self.path.source)
+
+
+class ReplaceDirective(Directive):
+ """Implementation of the ``py:replace`` template directive.
+
+ This directive replaces the element with the result of evaluating the
+ value of the ``py:replace`` attribute:
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/">
+ ... <span py:replace="bar">Hello</span>
+ ... </div>''')
+ >>> print(tmpl.generate(bar='Bye'))
+ <div>
+ Bye
+ </div>
+
+ This directive is equivalent to ``py:content`` combined with ``py:strip``,
+ providing a less verbose way to achieve the same effect:
+
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/">
+ ... <span py:content="bar" py:strip="">Hello</span>
+ ... </div>''')
+ >>> print(tmpl.generate(bar='Bye'))
+ <div>
+ Bye
+ </div>
+ """
+ __slots__ = []
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ value = value.get('value')
+ if not value:
+ raise TemplateSyntaxError('missing value for "replace" directive',
+ template.filepath, *pos[1:])
+ expr = cls._parse_expr(value, template, *pos[1:])
+ return None, [(EXPR, expr, pos)]
+
+
+class StripDirective(Directive):
+ """Implementation of the ``py:strip`` template directive.
+
+ When the value of the ``py:strip`` attribute evaluates to ``True``, the
+ element is stripped from the output
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/">
+ ... <div py:strip="True"><b>foo</b></div>
+ ... </div>''')
+ >>> print(tmpl.generate())
+ <div>
+ <b>foo</b>
+ </div>
+
+ Leaving the attribute value empty is equivalent to a truth value.
+
+ This directive is particulary interesting for named template functions or
+ match templates that do not generate a top-level element:
+
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/">
+ ... <div py:def="echo(what)" py:strip="">
+ ... <b>${what}</b>
+ ... </div>
+ ... ${echo('foo')}
+ ... </div>''')
+ >>> print(tmpl.generate())
+ <div>
+ <b>foo</b>
+ </div>
+ """
+ __slots__ = []
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ def _generate():
+ if not self.expr or _eval_expr(self.expr, ctxt, vars):
+ stream.next() # skip start tag
+ previous = stream.next()
+ for event in stream:
+ yield previous
+ previous = event
+ else:
+ for event in stream:
+ yield event
+ return _apply_directives(_generate(), directives, ctxt, vars)
+
+
+class ChooseDirective(Directive):
+ """Implementation of the ``py:choose`` directive for conditionally selecting
+ one of several body elements to display.
+
+ If the ``py:choose`` expression is empty the expressions of nested
+ ``py:when`` directives are tested for truth. The first true ``py:when``
+ body is output. If no ``py:when`` directive is matched then the fallback
+ directive ``py:otherwise`` will be used.
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/"
+ ... py:choose="">
+ ... <span py:when="0 == 1">0</span>
+ ... <span py:when="1 == 1">1</span>
+ ... <span py:otherwise="">2</span>
+ ... </div>''')
+ >>> print(tmpl.generate())
+ <div>
+ <span>1</span>
+ </div>
+
+ If the ``py:choose`` directive contains an expression, the nested
+ ``py:when`` directives are tested for equality to the ``py:choose``
+ expression:
+
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/"
+ ... py:choose="2">
+ ... <span py:when="1">1</span>
+ ... <span py:when="2">2</span>
+ ... </div>''')
+ >>> print(tmpl.generate())
+ <div>
+ <span>2</span>
+ </div>
+
+ Behavior is undefined if a ``py:choose`` block contains content outside a
+ ``py:when`` or ``py:otherwise`` block. Behavior is also undefined if a
+ ``py:otherwise`` occurs before ``py:when`` blocks.
+ """
+ __slots__ = ['matched', 'value']
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ value = value.get('test')
+ return super(ChooseDirective, cls).attach(template, stream, value,
+ namespaces, pos)
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ info = [False, bool(self.expr), None]
+ if self.expr:
+ info[2] = _eval_expr(self.expr, ctxt, vars)
+ ctxt._choice_stack.append(info)
+ for event in _apply_directives(stream, directives, ctxt, vars):
+ yield event
+ ctxt._choice_stack.pop()
+
+
+class WhenDirective(Directive):
+ """Implementation of the ``py:when`` directive for nesting in a parent with
+ the ``py:choose`` directive.
+
+ See the documentation of the `ChooseDirective` for usage.
+ """
+ __slots__ = ['filename']
+
+ def __init__(self, value, template, namespaces=None, lineno=-1, offset=-1):
+ Directive.__init__(self, value, template, namespaces, lineno, offset)
+ self.filename = template.filepath
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ value = value.get('test')
+ return super(WhenDirective, cls).attach(template, stream, value,
+ namespaces, pos)
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ info = ctxt._choice_stack and ctxt._choice_stack[-1]
+ if not info:
+ raise TemplateRuntimeError('"when" directives can only be used '
+ 'inside a "choose" directive',
+ self.filename, *stream.next()[2][1:])
+ if info[0]:
+ return []
+ if not self.expr and not info[1]:
+ raise TemplateRuntimeError('either "choose" or "when" directive '
+ 'must have a test expression',
+ self.filename, *stream.next()[2][1:])
+ if info[1]:
+ value = info[2]
+ if self.expr:
+ matched = value == _eval_expr(self.expr, ctxt, vars)
+ else:
+ matched = bool(value)
+ else:
+ matched = bool(_eval_expr(self.expr, ctxt, vars))
+ info[0] = matched
+ if not matched:
+ return []
+
+ return _apply_directives(stream, directives, ctxt, vars)
+
+
+class OtherwiseDirective(Directive):
+ """Implementation of the ``py:otherwise`` directive for nesting in a parent
+ with the ``py:choose`` directive.
+
+ See the documentation of `ChooseDirective` for usage.
+ """
+ __slots__ = ['filename']
+
+ def __init__(self, value, template, namespaces=None, lineno=-1, offset=-1):
+ Directive.__init__(self, None, template, namespaces, lineno, offset)
+ self.filename = template.filepath
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ info = ctxt._choice_stack and ctxt._choice_stack[-1]
+ if not info:
+ raise TemplateRuntimeError('an "otherwise" directive can only be '
+ 'used inside a "choose" directive',
+ self.filename, *stream.next()[2][1:])
+ if info[0]:
+ return []
+ info[0] = True
+
+ return _apply_directives(stream, directives, ctxt, vars)
+
+
+class WithDirective(Directive):
+ """Implementation of the ``py:with`` template directive, which allows
+ shorthand access to variables and expressions.
+
+ >>> from genshi.template import MarkupTemplate
+ >>> tmpl = MarkupTemplate('''<div xmlns:py="http://genshi.edgewall.org/">
+ ... <span py:with="y=7; z=x+10">$x $y $z</span>
+ ... </div>''')
+ >>> print(tmpl.generate(x=42))
+ <div>
+ <span>42 7 52</span>
+ </div>
+ """
+ __slots__ = ['vars']
+
+ def __init__(self, value, template, namespaces=None, lineno=-1, offset=-1):
+ Directive.__init__(self, None, template, namespaces, lineno, offset)
+ self.vars = []
+ value = value.strip()
+ try:
+ ast = _parse(value, 'exec')
+ for node in ast.body:
+ if not isinstance(node, _ast.Assign):
+ raise TemplateSyntaxError('only assignment allowed in '
+ 'value of the "with" directive',
+ template.filepath, lineno, offset)
+ self.vars.append(([_assignment(n) for n in node.targets],
+ Expression(node.value, template.filepath,
+ lineno, lookup=template.lookup)))
+ except SyntaxError, err:
+ err.msg += ' in expression "%s" of "%s" directive' % (value,
+ self.tagname)
+ raise TemplateSyntaxError(err, template.filepath, lineno,
+ offset + (err.offset or 0))
+
+ @classmethod
+ def attach(cls, template, stream, value, namespaces, pos):
+ if type(value) is dict:
+ value = value.get('vars')
+ return super(WithDirective, cls).attach(template, stream, value,
+ namespaces, pos)
+
+ def __call__(self, stream, directives, ctxt, **vars):
+ frame = {}
+ ctxt.push(frame)
+ for targets, expr in self.vars:
+ value = _eval_expr(expr, ctxt, vars)
+ for assign in targets:
+ assign(frame, value)
+ for event in _apply_directives(stream, directives, ctxt, vars):
+ yield event
+ ctxt.pop()
+
+ def __repr__(self):
+ return '<%s>' % (type(self).__name__)
diff --git a/websdk/genshi/template/eval.py b/websdk/genshi/template/eval.py
new file mode 100644
index 0000000..8593aaa
--- /dev/null
+++ b/websdk/genshi/template/eval.py
@@ -0,0 +1,629 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2010 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Support for "safe" evaluation of Python expressions."""
+
+import __builtin__
+
+from textwrap import dedent
+from types import CodeType
+
+from genshi.core import Markup
+from genshi.template.astutil import ASTTransformer, ASTCodeGenerator, \
+ _ast, parse
+from genshi.template.base import TemplateRuntimeError
+from genshi.util import flatten
+
+__all__ = ['Code', 'Expression', 'Suite', 'LenientLookup', 'StrictLookup',
+ 'Undefined', 'UndefinedError']
+__docformat__ = 'restructuredtext en'
+
+
+# Check for a Python 2.4 bug in the eval loop
+has_star_import_bug = False
+try:
+ class _FakeMapping(object):
+ __getitem__ = __setitem__ = lambda *a: None
+ exec 'from sys import *' in {}, _FakeMapping()
+except SystemError:
+ has_star_import_bug = True
+del _FakeMapping
+
+
+def _star_import_patch(mapping, modname):
+ """This function is used as helper if a Python version with a broken
+ star-import opcode is in use.
+ """
+ module = __import__(modname, None, None, ['__all__'])
+ if hasattr(module, '__all__'):
+ members = module.__all__
+ else:
+ members = [x for x in module.__dict__ if not x.startswith('_')]
+ mapping.update([(name, getattr(module, name)) for name in members])
+
+
+class Code(object):
+ """Abstract base class for the `Expression` and `Suite` classes."""
+ __slots__ = ['source', 'code', 'ast', '_globals']
+
+ def __init__(self, source, filename=None, lineno=-1, lookup='strict',
+ xform=None):
+ """Create the code object, either from a string, or from an AST node.
+
+ :param source: either a string containing the source code, or an AST
+ node
+ :param filename: the (preferably absolute) name of the file containing
+ the code
+ :param lineno: the number of the line on which the code was found
+ :param lookup: the lookup class that defines how variables are looked
+ up in the context; can be either "strict" (the default),
+ "lenient", or a custom lookup class
+ :param xform: the AST transformer that should be applied to the code;
+ if `None`, the appropriate transformation is chosen
+ depending on the mode
+ """
+ if isinstance(source, basestring):
+ self.source = source
+ node = _parse(source, mode=self.mode)
+ else:
+ assert isinstance(source, _ast.AST), \
+ 'Expected string or AST node, but got %r' % source
+ self.source = '?'
+ if self.mode == 'eval':
+ node = _ast.Expression()
+ node.body = source
+ else:
+ node = _ast.Module()
+ node.body = [source]
+
+ self.ast = node
+ self.code = _compile(node, self.source, mode=self.mode,
+ filename=filename, lineno=lineno, xform=xform)
+ if lookup is None:
+ lookup = LenientLookup
+ elif isinstance(lookup, basestring):
+ lookup = {'lenient': LenientLookup, 'strict': StrictLookup}[lookup]
+ self._globals = lookup.globals
+
+ def __getstate__(self):
+ state = {'source': self.source, 'ast': self.ast,
+ 'lookup': self._globals.im_self}
+ c = self.code
+ state['code'] = (c.co_nlocals, c.co_stacksize, c.co_flags, c.co_code,
+ c.co_consts, c.co_names, c.co_varnames, c.co_filename,
+ c.co_name, c.co_firstlineno, c.co_lnotab, (), ())
+ return state
+
+ def __setstate__(self, state):
+ self.source = state['source']
+ self.ast = state['ast']
+ self.code = CodeType(0, *state['code'])
+ self._globals = state['lookup'].globals
+
+ def __eq__(self, other):
+ return (type(other) == type(self)) and (self.code == other.code)
+
+ def __hash__(self):
+ return hash(self.code)
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __repr__(self):
+ return '%s(%r)' % (type(self).__name__, self.source)
+
+
+class Expression(Code):
+ """Evaluates Python expressions used in templates.
+
+ >>> data = dict(test='Foo', items=[1, 2, 3], dict={'some': 'thing'})
+ >>> Expression('test').evaluate(data)
+ 'Foo'
+
+ >>> Expression('items[0]').evaluate(data)
+ 1
+ >>> Expression('items[-1]').evaluate(data)
+ 3
+ >>> Expression('dict["some"]').evaluate(data)
+ 'thing'
+
+ Similar to e.g. Javascript, expressions in templates can use the dot
+ notation for attribute access to access items in mappings:
+
+ >>> Expression('dict.some').evaluate(data)
+ 'thing'
+
+ This also works the other way around: item access can be used to access
+ any object attribute:
+
+ >>> class MyClass(object):
+ ... myattr = 'Bar'
+ >>> data = dict(mine=MyClass(), key='myattr')
+ >>> Expression('mine.myattr').evaluate(data)
+ 'Bar'
+ >>> Expression('mine["myattr"]').evaluate(data)
+ 'Bar'
+ >>> Expression('mine[key]').evaluate(data)
+ 'Bar'
+
+ All of the standard Python operators are available to template expressions.
+ Built-in functions such as ``len()`` are also available in template
+ expressions:
+
+ >>> data = dict(items=[1, 2, 3])
+ >>> Expression('len(items)').evaluate(data)
+ 3
+ """
+ __slots__ = []
+ mode = 'eval'
+
+ def evaluate(self, data):
+ """Evaluate the expression against the given data dictionary.
+
+ :param data: a mapping containing the data to evaluate against
+ :return: the result of the evaluation
+ """
+ __traceback_hide__ = 'before_and_this'
+ _globals = self._globals(data)
+ return eval(self.code, _globals, {'__data__': data})
+
+
+class Suite(Code):
+ """Executes Python statements used in templates.
+
+ >>> data = dict(test='Foo', items=[1, 2, 3], dict={'some': 'thing'})
+ >>> Suite("foo = dict['some']").execute(data)
+ >>> data['foo']
+ 'thing'
+ """
+ __slots__ = []
+ mode = 'exec'
+
+ def execute(self, data):
+ """Execute the suite in the given data dictionary.
+
+ :param data: a mapping containing the data to execute in
+ """
+ __traceback_hide__ = 'before_and_this'
+ _globals = self._globals(data)
+ exec self.code in _globals, data
+
+
+UNDEFINED = object()
+
+
+class UndefinedError(TemplateRuntimeError):
+ """Exception thrown when a template expression attempts to access a variable
+ not defined in the context.
+
+ :see: `LenientLookup`, `StrictLookup`
+ """
+ def __init__(self, name, owner=UNDEFINED):
+ if owner is not UNDEFINED:
+ message = '%s has no member named "%s"' % (repr(owner), name)
+ else:
+ message = '"%s" not defined' % name
+ TemplateRuntimeError.__init__(self, message)
+
+
+class Undefined(object):
+ """Represents a reference to an undefined variable.
+
+ Unlike the Python runtime, template expressions can refer to an undefined
+ variable without causing a `NameError` to be raised. The result will be an
+ instance of the `Undefined` class, which is treated the same as ``False`` in
+ conditions, but raise an exception on any other operation:
+
+ >>> foo = Undefined('foo')
+ >>> bool(foo)
+ False
+ >>> list(foo)
+ []
+ >>> print(foo)
+ undefined
+
+ However, calling an undefined variable, or trying to access an attribute
+ of that variable, will raise an exception that includes the name used to
+ reference that undefined variable.
+
+ >>> foo('bar')
+ Traceback (most recent call last):
+ ...
+ UndefinedError: "foo" not defined
+
+ >>> foo.bar
+ Traceback (most recent call last):
+ ...
+ UndefinedError: "foo" not defined
+
+ :see: `LenientLookup`
+ """
+ __slots__ = ['_name', '_owner']
+
+ def __init__(self, name, owner=UNDEFINED):
+ """Initialize the object.
+
+ :param name: the name of the reference
+ :param owner: the owning object, if the variable is accessed as a member
+ """
+ self._name = name
+ self._owner = owner
+
+ def __iter__(self):
+ return iter([])
+
+ def __nonzero__(self):
+ return False
+
+ def __repr__(self):
+ return '<%s %r>' % (type(self).__name__, self._name)
+
+ def __str__(self):
+ return 'undefined'
+
+ def _die(self, *args, **kwargs):
+ """Raise an `UndefinedError`."""
+ __traceback_hide__ = True
+ raise UndefinedError(self._name, self._owner)
+ __call__ = __getattr__ = __getitem__ = _die
+
+ # Hack around some behavior introduced in Python 2.6.2
+ # http://genshi.edgewall.org/ticket/324
+ __length_hint__ = None
+
+
+class LookupBase(object):
+ """Abstract base class for variable lookup implementations."""
+
+ @classmethod
+ def globals(cls, data):
+ """Construct the globals dictionary to use as the execution context for
+ the expression or suite.
+ """
+ return {
+ '__data__': data,
+ '_lookup_name': cls.lookup_name,
+ '_lookup_attr': cls.lookup_attr,
+ '_lookup_item': cls.lookup_item,
+ '_star_import_patch': _star_import_patch,
+ 'UndefinedError': UndefinedError,
+ }
+
+ @classmethod
+ def lookup_name(cls, data, name):
+ __traceback_hide__ = True
+ val = data.get(name, UNDEFINED)
+ if val is UNDEFINED:
+ val = BUILTINS.get(name, val)
+ if val is UNDEFINED:
+ val = cls.undefined(name)
+ return val
+
+ @classmethod
+ def lookup_attr(cls, obj, key):
+ __traceback_hide__ = True
+ try:
+ val = getattr(obj, key)
+ except AttributeError:
+ if hasattr(obj.__class__, key):
+ raise
+ else:
+ try:
+ val = obj[key]
+ except (KeyError, TypeError):
+ val = cls.undefined(key, owner=obj)
+ return val
+
+ @classmethod
+ def lookup_item(cls, obj, key):
+ __traceback_hide__ = True
+ if len(key) == 1:
+ key = key[0]
+ try:
+ return obj[key]
+ except (AttributeError, KeyError, IndexError, TypeError), e:
+ if isinstance(key, basestring):
+ val = getattr(obj, key, UNDEFINED)
+ if val is UNDEFINED:
+ val = cls.undefined(key, owner=obj)
+ return val
+ raise
+
+ @classmethod
+ def undefined(cls, key, owner=UNDEFINED):
+ """Can be overridden by subclasses to specify behavior when undefined
+ variables are accessed.
+
+ :param key: the name of the variable
+ :param owner: the owning object, if the variable is accessed as a member
+ """
+ raise NotImplementedError
+
+
+class LenientLookup(LookupBase):
+ """Default variable lookup mechanism for expressions.
+
+ When an undefined variable is referenced using this lookup style, the
+ reference evaluates to an instance of the `Undefined` class:
+
+ >>> expr = Expression('nothing', lookup='lenient')
+ >>> undef = expr.evaluate({})
+ >>> undef
+ <Undefined 'nothing'>
+
+ The same will happen when a non-existing attribute or item is accessed on
+ an existing object:
+
+ >>> expr = Expression('something.nil', lookup='lenient')
+ >>> expr.evaluate({'something': dict()})
+ <Undefined 'nil'>
+
+ See the documentation of the `Undefined` class for details on the behavior
+ of such objects.
+
+ :see: `StrictLookup`
+ """
+
+ @classmethod
+ def undefined(cls, key, owner=UNDEFINED):
+ """Return an ``Undefined`` object."""
+ __traceback_hide__ = True
+ return Undefined(key, owner=owner)
+
+
+class StrictLookup(LookupBase):
+ """Strict variable lookup mechanism for expressions.
+
+ Referencing an undefined variable using this lookup style will immediately
+ raise an ``UndefinedError``:
+
+ >>> expr = Expression('nothing', lookup='strict')
+ >>> expr.evaluate({})
+ Traceback (most recent call last):
+ ...
+ UndefinedError: "nothing" not defined
+
+ The same happens when a non-existing attribute or item is accessed on an
+ existing object:
+
+ >>> expr = Expression('something.nil', lookup='strict')
+ >>> expr.evaluate({'something': dict()})
+ Traceback (most recent call last):
+ ...
+ UndefinedError: {} has no member named "nil"
+ """
+
+ @classmethod
+ def undefined(cls, key, owner=UNDEFINED):
+ """Raise an ``UndefinedError`` immediately."""
+ __traceback_hide__ = True
+ raise UndefinedError(key, owner=owner)
+
+
+def _parse(source, mode='eval'):
+ source = source.strip()
+ if mode == 'exec':
+ lines = [line.expandtabs() for line in source.splitlines()]
+ if lines:
+ first = lines[0]
+ rest = dedent('\n'.join(lines[1:])).rstrip()
+ if first.rstrip().endswith(':') and not rest[0].isspace():
+ rest = '\n'.join([' %s' % line for line in rest.splitlines()])
+ source = '\n'.join([first, rest])
+ if isinstance(source, unicode):
+ source = '\xef\xbb\xbf' + source.encode('utf-8')
+ return parse(source, mode)
+
+
+def _compile(node, source=None, mode='eval', filename=None, lineno=-1,
+ xform=None):
+ if isinstance(filename, unicode):
+ # unicode file names not allowed for code objects
+ filename = filename.encode('utf-8', 'replace')
+ elif not filename:
+ filename = '<string>'
+ if lineno <= 0:
+ lineno = 1
+
+ if xform is None:
+ xform = {
+ 'eval': ExpressionASTTransformer
+ }.get(mode, TemplateASTTransformer)
+ tree = xform().visit(node)
+
+ if mode == 'eval':
+ name = '<Expression %r>' % (source or '?')
+ else:
+ lines = source.splitlines()
+ if not lines:
+ extract = ''
+ else:
+ extract = lines[0]
+ if len(lines) > 1:
+ extract += ' ...'
+ name = '<Suite %r>' % (extract)
+ new_source = ASTCodeGenerator(tree).code
+ code = compile(new_source, filename, mode)
+
+ try:
+ # We'd like to just set co_firstlineno, but it's readonly. So we need
+ # to clone the code object while adjusting the line number
+ return CodeType(0, code.co_nlocals, code.co_stacksize,
+ code.co_flags | 0x0040, code.co_code, code.co_consts,
+ code.co_names, code.co_varnames, filename, name,
+ lineno, code.co_lnotab, (), ())
+ except RuntimeError:
+ return code
+
+
+def _new(class_, *args, **kwargs):
+ ret = class_()
+ for attr, value in zip(ret._fields, args):
+ if attr in kwargs:
+ raise ValueError('Field set both in args and kwargs')
+ setattr(ret, attr, value)
+ for attr, value in kwargs:
+ setattr(ret, attr, value)
+ return ret
+
+
+BUILTINS = __builtin__.__dict__.copy()
+BUILTINS.update({'Markup': Markup, 'Undefined': Undefined})
+CONSTANTS = frozenset(['False', 'True', 'None', 'NotImplemented', 'Ellipsis'])
+
+
+class TemplateASTTransformer(ASTTransformer):
+ """Concrete AST transformer that implements the AST transformations needed
+ for code embedded in templates.
+ """
+
+ def __init__(self):
+ self.locals = [CONSTANTS]
+
+ def _extract_names(self, node):
+ names = set()
+ def _process(node):
+ if isinstance(node, _ast.Name):
+ names.add(node.id)
+ elif isinstance(node, _ast.alias):
+ names.add(node.asname or node.name)
+ elif isinstance(node, _ast.Tuple):
+ for elt in node.elts:
+ _process(elt)
+ if hasattr(node, 'args'):
+ for arg in node.args:
+ _process(arg)
+ if hasattr(node, 'vararg'):
+ names.add(node.vararg)
+ if hasattr(node, 'kwarg'):
+ names.add(node.kwarg)
+ elif hasattr(node, 'names'):
+ for elt in node.names:
+ _process(elt)
+ return names
+
+ def visit_Str(self, node):
+ if isinstance(node.s, str):
+ try: # If the string is ASCII, return a `str` object
+ node.s.decode('ascii')
+ except ValueError: # Otherwise return a `unicode` object
+ return _new(_ast.Str, node.s.decode('utf-8'))
+ return node
+
+ def visit_ClassDef(self, node):
+ if len(self.locals) > 1:
+ self.locals[-1].add(node.name)
+ self.locals.append(set())
+ try:
+ return ASTTransformer.visit_ClassDef(self, node)
+ finally:
+ self.locals.pop()
+
+ def visit_Import(self, node):
+ if len(self.locals) > 1:
+ self.locals[-1].update(self._extract_names(node))
+ return ASTTransformer.visit_Import(self, node)
+
+ def visit_ImportFrom(self, node):
+ if [a.name for a in node.names] == ['*']:
+ if has_star_import_bug:
+ # This is a Python 2.4 bug. Only if we have a broken Python
+ # version do we need to apply this hack
+ node = _new(_ast.Expr, _new(_ast.Call,
+ _new(_ast.Name, '_star_import_patch'), [
+ _new(_ast.Name, '__data__'),
+ _new(_ast.Str, node.module)
+ ], (), ()))
+ return node
+ if len(self.locals) > 1:
+ self.locals[-1].update(self._extract_names(node))
+ return ASTTransformer.visit_ImportFrom(self, node)
+
+ def visit_FunctionDef(self, node):
+ if len(self.locals) > 1:
+ self.locals[-1].add(node.name)
+
+ self.locals.append(self._extract_names(node.args))
+ try:
+ return ASTTransformer.visit_FunctionDef(self, node)
+ finally:
+ self.locals.pop()
+
+ # GeneratorExp(expr elt, comprehension* generators)
+ def visit_GeneratorExp(self, node):
+ gens = []
+ for generator in node.generators:
+ # comprehension = (expr target, expr iter, expr* ifs)
+ self.locals.append(set())
+ gen = _new(_ast.comprehension, self.visit(generator.target),
+ self.visit(generator.iter),
+ [self.visit(if_) for if_ in generator.ifs])
+ gens.append(gen)
+
+ # use node.__class__ to make it reusable as ListComp
+ ret = _new(node.__class__, self.visit(node.elt), gens)
+ #delete inserted locals
+ del self.locals[-len(node.generators):]
+ return ret
+
+ # ListComp(expr elt, comprehension* generators)
+ visit_ListComp = visit_GeneratorExp
+
+ def visit_Lambda(self, node):
+ self.locals.append(self._extract_names(node.args))
+ try:
+ return ASTTransformer.visit_Lambda(self, node)
+ finally:
+ self.locals.pop()
+
+ def visit_Name(self, node):
+ # If the name refers to a local inside a lambda, list comprehension, or
+ # generator expression, leave it alone
+ if isinstance(node.ctx, _ast.Load) and \
+ node.id not in flatten(self.locals):
+ # Otherwise, translate the name ref into a context lookup
+ name = _new(_ast.Name, '_lookup_name', _ast.Load())
+ namearg = _new(_ast.Name, '__data__', _ast.Load())
+ strarg = _new(_ast.Str, node.id)
+ node = _new(_ast.Call, name, [namearg, strarg], [])
+ elif isinstance(node.ctx, _ast.Store):
+ if len(self.locals) > 1:
+ self.locals[-1].add(node.id)
+
+ return node
+
+
+class ExpressionASTTransformer(TemplateASTTransformer):
+ """Concrete AST transformer that implements the AST transformations needed
+ for code embedded in templates.
+ """
+
+ def visit_Attribute(self, node):
+ if not isinstance(node.ctx, _ast.Load):
+ return ASTTransformer.visit_Attribute(self, node)
+
+ func = _new(_ast.Name, '_lookup_attr', _ast.Load())
+ args = [self.visit(node.value), _new(_ast.Str, node.attr)]
+ return _new(_ast.Call, func, args, [])
+
+ def visit_Subscript(self, node):
+ if not isinstance(node.ctx, _ast.Load) or \
+ not isinstance(node.slice, _ast.Index):
+ return ASTTransformer.visit_Subscript(self, node)
+
+ func = _new(_ast.Name, '_lookup_item', _ast.Load())
+ args = [
+ self.visit(node.value),
+ _new(_ast.Tuple, (self.visit(node.slice.value),), _ast.Load())
+ ]
+ return _new(_ast.Call, func, args, [])
diff --git a/websdk/genshi/template/interpolation.py b/websdk/genshi/template/interpolation.py
new file mode 100644
index 0000000..1e1a385
--- /dev/null
+++ b/websdk/genshi/template/interpolation.py
@@ -0,0 +1,153 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""String interpolation routines, i.e. the splitting up a given text into some
+parts that are literal strings, and others that are Python expressions.
+"""
+
+from itertools import chain
+import os
+import re
+from tokenize import PseudoToken
+
+from genshi.core import TEXT
+from genshi.template.base import TemplateSyntaxError, EXPR
+from genshi.template.eval import Expression
+
+__all__ = ['interpolate']
+__docformat__ = 'restructuredtext en'
+
+NAMESTART = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
+NAMECHARS = NAMESTART + '.0123456789'
+PREFIX = '$'
+
+token_re = re.compile('%s|%s(?s)' % (
+ r'[uU]?[rR]?("""|\'\'\')((?<!\\)\\\1|.)*?\1',
+ PseudoToken
+))
+
+
+def interpolate(text, filepath=None, lineno=-1, offset=0, lookup='strict'):
+ """Parse the given string and extract expressions.
+
+ This function is a generator that yields `TEXT` events for literal strings,
+ and `EXPR` events for expressions, depending on the results of parsing the
+ string.
+
+ >>> for kind, data, pos in interpolate("hey ${foo}bar"):
+ ... print('%s %r' % (kind, data))
+ TEXT 'hey '
+ EXPR Expression('foo')
+ TEXT 'bar'
+
+ :param text: the text to parse
+ :param filepath: absolute path to the file in which the text was found
+ (optional)
+ :param lineno: the line number at which the text was found (optional)
+ :param offset: the column number at which the text starts in the source
+ (optional)
+ :param lookup: the variable lookup mechanism; either "lenient" (the
+ default), "strict", or a custom lookup class
+ :return: a list of `TEXT` and `EXPR` events
+ :raise TemplateSyntaxError: when a syntax error in an expression is
+ encountered
+ """
+ pos = [filepath, lineno, offset]
+
+ textbuf = []
+ textpos = None
+ for is_expr, chunk in chain(lex(text, pos, filepath), [(True, '')]):
+ if is_expr:
+ if textbuf:
+ yield TEXT, ''.join(textbuf), textpos
+ del textbuf[:]
+ textpos = None
+ if chunk:
+ try:
+ expr = Expression(chunk.strip(), pos[0], pos[1],
+ lookup=lookup)
+ yield EXPR, expr, tuple(pos)
+ except SyntaxError, err:
+ raise TemplateSyntaxError(err, filepath, pos[1],
+ pos[2] + (err.offset or 0))
+ else:
+ textbuf.append(chunk)
+ if textpos is None:
+ textpos = tuple(pos)
+
+ if '\n' in chunk:
+ lines = chunk.splitlines()
+ pos[1] += len(lines) - 1
+ pos[2] += len(lines[-1])
+ else:
+ pos[2] += len(chunk)
+
+
+def lex(text, textpos, filepath):
+ offset = pos = 0
+ end = len(text)
+ escaped = False
+
+ while 1:
+ if escaped:
+ offset = text.find(PREFIX, offset + 2)
+ escaped = False
+ else:
+ offset = text.find(PREFIX, pos)
+ if offset < 0 or offset == end - 1:
+ break
+ next = text[offset + 1]
+
+ if next == '{':
+ if offset > pos:
+ yield False, text[pos:offset]
+ pos = offset + 2
+ level = 1
+ while level:
+ match = token_re.match(text, pos)
+ if match is None:
+ raise TemplateSyntaxError('invalid syntax', filepath,
+ *textpos[1:])
+ pos = match.end()
+ tstart, tend = match.regs[3]
+ token = text[tstart:tend]
+ if token == '{':
+ level += 1
+ elif token == '}':
+ level -= 1
+ yield True, text[offset + 2:pos - 1]
+
+ elif next in NAMESTART:
+ if offset > pos:
+ yield False, text[pos:offset]
+ pos = offset
+ pos += 1
+ while pos < end:
+ char = text[pos]
+ if char not in NAMECHARS:
+ break
+ pos += 1
+ yield True, text[offset + 1:pos].strip()
+
+ elif not escaped and next == PREFIX:
+ if offset > pos:
+ yield False, text[pos:offset]
+ escaped = True
+ pos = offset + 1
+
+ else:
+ yield False, text[pos:offset + 1]
+ pos = offset + 1
+
+ if pos < end:
+ yield False, text[pos:]
diff --git a/websdk/genshi/template/loader.py b/websdk/genshi/template/loader.py
new file mode 100644
index 0000000..0e7cda7
--- /dev/null
+++ b/websdk/genshi/template/loader.py
@@ -0,0 +1,344 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2010 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Template loading and caching."""
+
+import os
+try:
+ import threading
+except ImportError:
+ import dummy_threading as threading
+
+from genshi.template.base import TemplateError
+from genshi.util import LRUCache
+
+__all__ = ['TemplateLoader', 'TemplateNotFound', 'directory', 'package',
+ 'prefixed']
+__docformat__ = 'restructuredtext en'
+
+
+class TemplateNotFound(TemplateError):
+ """Exception raised when a specific template file could not be found."""
+
+ def __init__(self, name, search_path):
+ """Create the exception.
+
+ :param name: the filename of the template
+ :param search_path: the search path used to lookup the template
+ """
+ TemplateError.__init__(self, 'Template "%s" not found' % name)
+ self.search_path = search_path
+
+
+class TemplateLoader(object):
+ """Responsible for loading templates from files on the specified search
+ path.
+
+ >>> import tempfile
+ >>> fd, path = tempfile.mkstemp(suffix='.html', prefix='template')
+ >>> os.write(fd, '<p>$var</p>')
+ 11
+ >>> os.close(fd)
+
+ The template loader accepts a list of directory paths that are then used
+ when searching for template files, in the given order:
+
+ >>> loader = TemplateLoader([os.path.dirname(path)])
+
+ The `load()` method first checks the template cache whether the requested
+ template has already been loaded. If not, it attempts to locate the
+ template file, and returns the corresponding `Template` object:
+
+ >>> from genshi.template import MarkupTemplate
+ >>> template = loader.load(os.path.basename(path))
+ >>> isinstance(template, MarkupTemplate)
+ True
+
+ Template instances are cached: requesting a template with the same name
+ results in the same instance being returned:
+
+ >>> loader.load(os.path.basename(path)) is template
+ True
+
+ The `auto_reload` option can be used to control whether a template should
+ be automatically reloaded when the file it was loaded from has been
+ changed. Disable this automatic reloading to improve performance.
+
+ >>> os.remove(path)
+ """
+ def __init__(self, search_path=None, auto_reload=False,
+ default_encoding=None, max_cache_size=25, default_class=None,
+ variable_lookup='strict', allow_exec=True, callback=None):
+ """Create the template laoder.
+
+ :param search_path: a list of absolute path names that should be
+ searched for template files, or a string containing
+ a single absolute path; alternatively, any item on
+ the list may be a ''load function'' that is passed
+ a filename and returns a file-like object and some
+ metadata
+ :param auto_reload: whether to check the last modification time of
+ template files, and reload them if they have changed
+ :param default_encoding: the default encoding to assume when loading
+ templates; defaults to UTF-8
+ :param max_cache_size: the maximum number of templates to keep in the
+ cache
+ :param default_class: the default `Template` subclass to use when
+ instantiating templates
+ :param variable_lookup: the variable lookup mechanism; either "strict"
+ (the default), "lenient", or a custom lookup
+ class
+ :param allow_exec: whether to allow Python code blocks in templates
+ :param callback: (optional) a callback function that is invoked after a
+ template was initialized by this loader; the function
+ is passed the template object as only argument. This
+ callback can be used for example to add any desired
+ filters to the template
+ :see: `LenientLookup`, `StrictLookup`
+
+ :note: Changed in 0.5: Added the `allow_exec` argument
+ """
+ from genshi.template.markup import MarkupTemplate
+
+ self.search_path = search_path
+ if self.search_path is None:
+ self.search_path = []
+ elif not isinstance(self.search_path, (list, tuple)):
+ self.search_path = [self.search_path]
+
+ self.auto_reload = auto_reload
+ """Whether templates should be reloaded when the underlying file is
+ changed"""
+
+ self.default_encoding = default_encoding
+ self.default_class = default_class or MarkupTemplate
+ self.variable_lookup = variable_lookup
+ self.allow_exec = allow_exec
+ if callback is not None and not hasattr(callback, '__call__'):
+ raise TypeError('The "callback" parameter needs to be callable')
+ self.callback = callback
+ self._cache = LRUCache(max_cache_size)
+ self._uptodate = {}
+ self._lock = threading.RLock()
+
+ def __getstate__(self):
+ state = self.__dict__.copy()
+ state['_lock'] = None
+ return state
+
+ def __setstate__(self, state):
+ self.__dict__ = state
+ self._lock = threading.RLock()
+
+ def load(self, filename, relative_to=None, cls=None, encoding=None):
+ """Load the template with the given name.
+
+ If the `filename` parameter is relative, this method searches the
+ search path trying to locate a template matching the given name. If the
+ file name is an absolute path, the search path is ignored.
+
+ If the requested template is not found, a `TemplateNotFound` exception
+ is raised. Otherwise, a `Template` object is returned that represents
+ the parsed template.
+
+ Template instances are cached to avoid having to parse the same
+ template file more than once. Thus, subsequent calls of this method
+ with the same template file name will return the same `Template`
+ object (unless the ``auto_reload`` option is enabled and the file was
+ changed since the last parse.)
+
+ If the `relative_to` parameter is provided, the `filename` is
+ interpreted as being relative to that path.
+
+ :param filename: the relative path of the template file to load
+ :param relative_to: the filename of the template from which the new
+ template is being loaded, or ``None`` if the
+ template is being loaded directly
+ :param cls: the class of the template object to instantiate
+ :param encoding: the encoding of the template to load; defaults to the
+ ``default_encoding`` of the loader instance
+ :return: the loaded `Template` instance
+ :raises TemplateNotFound: if a template with the given name could not
+ be found
+ """
+ if cls is None:
+ cls = self.default_class
+ search_path = self.search_path
+
+ # Make the filename relative to the template file its being loaded
+ # from, but only if that file is specified as a relative path, or no
+ # search path has been set up
+ if relative_to and (not search_path or not os.path.isabs(relative_to)):
+ filename = os.path.join(os.path.dirname(relative_to), filename)
+
+ filename = os.path.normpath(filename)
+ cachekey = filename
+
+ self._lock.acquire()
+ try:
+ # First check the cache to avoid reparsing the same file
+ try:
+ tmpl = self._cache[cachekey]
+ if not self.auto_reload:
+ return tmpl
+ uptodate = self._uptodate[cachekey]
+ if uptodate is not None and uptodate():
+ return tmpl
+ except (KeyError, OSError):
+ pass
+
+ isabs = False
+
+ if os.path.isabs(filename):
+ # Bypass the search path if the requested filename is absolute
+ search_path = [os.path.dirname(filename)]
+ isabs = True
+
+ elif relative_to and os.path.isabs(relative_to):
+ # Make sure that the directory containing the including
+ # template is on the search path
+ dirname = os.path.dirname(relative_to)
+ if dirname not in search_path:
+ search_path = list(search_path) + [dirname]
+ isabs = True
+
+ elif not search_path:
+ # Uh oh, don't know where to look for the template
+ raise TemplateError('Search path for templates not configured')
+
+ for loadfunc in search_path:
+ if isinstance(loadfunc, basestring):
+ loadfunc = directory(loadfunc)
+ try:
+ filepath, filename, fileobj, uptodate = loadfunc(filename)
+ except IOError:
+ continue
+ else:
+ try:
+ if isabs:
+ # If the filename of either the included or the
+ # including template is absolute, make sure the
+ # included template gets an absolute path, too,
+ # so that nested includes work properly without a
+ # search path
+ filename = filepath
+ tmpl = self._instantiate(cls, fileobj, filepath,
+ filename, encoding=encoding)
+ if self.callback:
+ self.callback(tmpl)
+ self._cache[cachekey] = tmpl
+ self._uptodate[cachekey] = uptodate
+ finally:
+ if hasattr(fileobj, 'close'):
+ fileobj.close()
+ return tmpl
+
+ raise TemplateNotFound(filename, search_path)
+
+ finally:
+ self._lock.release()
+
+ def _instantiate(self, cls, fileobj, filepath, filename, encoding=None):
+ """Instantiate and return the `Template` object based on the given
+ class and parameters.
+
+ This function is intended for subclasses to override if they need to
+ implement special template instantiation logic. Code that just uses
+ the `TemplateLoader` should use the `load` method instead.
+
+ :param cls: the class of the template object to instantiate
+ :param fileobj: a readable file-like object containing the template
+ source
+ :param filepath: the absolute path to the template file
+ :param filename: the path to the template file relative to the search
+ path
+ :param encoding: the encoding of the template to load; defaults to the
+ ``default_encoding`` of the loader instance
+ :return: the loaded `Template` instance
+ :rtype: `Template`
+ """
+ if encoding is None:
+ encoding = self.default_encoding
+ return cls(fileobj, filepath=filepath, filename=filename, loader=self,
+ encoding=encoding, lookup=self.variable_lookup,
+ allow_exec=self.allow_exec)
+
+ @staticmethod
+ def directory(path):
+ """Loader factory for loading templates from a local directory.
+
+ :param path: the path to the local directory containing the templates
+ :return: the loader function to load templates from the given directory
+ :rtype: ``function``
+ """
+ def _load_from_directory(filename):
+ filepath = os.path.join(path, filename)
+ fileobj = open(filepath, 'U')
+ mtime = os.path.getmtime(filepath)
+ def _uptodate():
+ return mtime == os.path.getmtime(filepath)
+ return filepath, filename, fileobj, _uptodate
+ return _load_from_directory
+
+ @staticmethod
+ def package(name, path):
+ """Loader factory for loading templates from egg package data.
+
+ :param name: the name of the package containing the resources
+ :param path: the path inside the package data
+ :return: the loader function to load templates from the given package
+ :rtype: ``function``
+ """
+ from pkg_resources import resource_stream
+ def _load_from_package(filename):
+ filepath = os.path.join(path, filename)
+ return filepath, filename, resource_stream(name, filepath), None
+ return _load_from_package
+
+ @staticmethod
+ def prefixed(**delegates):
+ """Factory for a load function that delegates to other loaders
+ depending on the prefix of the requested template path.
+
+ The prefix is stripped from the filename when passing on the load
+ request to the delegate.
+
+ >>> load = prefixed(
+ ... app1 = lambda filename: ('app1', filename, None, None),
+ ... app2 = lambda filename: ('app2', filename, None, None)
+ ... )
+ >>> print(load('app1/foo.html'))
+ ('app1', 'app1/foo.html', None, None)
+ >>> print(load('app2/bar.html'))
+ ('app2', 'app2/bar.html', None, None)
+
+ :param delegates: mapping of path prefixes to loader functions
+ :return: the loader function
+ :rtype: ``function``
+ """
+ def _dispatch_by_prefix(filename):
+ for prefix, delegate in delegates.items():
+ if filename.startswith(prefix):
+ if isinstance(delegate, basestring):
+ delegate = directory(delegate)
+ filepath, _, fileobj, uptodate = delegate(
+ filename[len(prefix):].lstrip('/\\')
+ )
+ return filepath, filename, fileobj, uptodate
+ raise TemplateNotFound(filename, list(delegates.keys()))
+ return _dispatch_by_prefix
+
+
+directory = TemplateLoader.directory
+package = TemplateLoader.package
+prefixed = TemplateLoader.prefixed
diff --git a/websdk/genshi/template/markup.py b/websdk/genshi/template/markup.py
new file mode 100644
index 0000000..0e31632
--- /dev/null
+++ b/websdk/genshi/template/markup.py
@@ -0,0 +1,397 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2010 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Markup templating engine."""
+
+from itertools import chain
+
+from genshi.core import Attrs, Markup, Namespace, Stream, StreamEventKind
+from genshi.core import START, END, START_NS, END_NS, TEXT, PI, COMMENT
+from genshi.input import XMLParser
+from genshi.template.base import BadDirectiveError, Template, \
+ TemplateSyntaxError, _apply_directives, \
+ EXEC, INCLUDE, SUB
+from genshi.template.eval import Suite
+from genshi.template.interpolation import interpolate
+from genshi.template.directives import *
+from genshi.template.text import NewTextTemplate
+
+__all__ = ['MarkupTemplate']
+__docformat__ = 'restructuredtext en'
+
+
+class MarkupTemplate(Template):
+ """Implementation of the template language for XML-based templates.
+
+ >>> tmpl = MarkupTemplate('''<ul xmlns:py="http://genshi.edgewall.org/">
+ ... <li py:for="item in items">${item}</li>
+ ... </ul>''')
+ >>> print(tmpl.generate(items=[1, 2, 3]))
+ <ul>
+ <li>1</li><li>2</li><li>3</li>
+ </ul>
+ """
+
+ DIRECTIVE_NAMESPACE = 'http://genshi.edgewall.org/'
+ XINCLUDE_NAMESPACE = 'http://www.w3.org/2001/XInclude'
+
+ directives = [('def', DefDirective),
+ ('match', MatchDirective),
+ ('when', WhenDirective),
+ ('otherwise', OtherwiseDirective),
+ ('for', ForDirective),
+ ('if', IfDirective),
+ ('choose', ChooseDirective),
+ ('with', WithDirective),
+ ('replace', ReplaceDirective),
+ ('content', ContentDirective),
+ ('attrs', AttrsDirective),
+ ('strip', StripDirective)]
+ serializer = 'xml'
+ _number_conv = Markup
+
+ def __init__(self, source, filepath=None, filename=None, loader=None,
+ encoding=None, lookup='strict', allow_exec=True):
+ Template.__init__(self, source, filepath=filepath, filename=filename,
+ loader=loader, encoding=encoding, lookup=lookup,
+ allow_exec=allow_exec)
+ self.add_directives(self.DIRECTIVE_NAMESPACE, self)
+
+ def _init_filters(self):
+ Template._init_filters(self)
+ # Make sure the include filter comes after the match filter
+ self.filters.remove(self._include)
+ self.filters += [self._match, self._include]
+
+ def _parse(self, source, encoding):
+ if not isinstance(source, Stream):
+ source = XMLParser(source, filename=self.filename,
+ encoding=encoding)
+ stream = []
+
+ for kind, data, pos in source:
+
+ if kind is TEXT:
+ for kind, data, pos in interpolate(data, self.filepath, pos[1],
+ pos[2], lookup=self.lookup):
+ stream.append((kind, data, pos))
+
+ elif kind is PI and data[0] == 'python':
+ if not self.allow_exec:
+ raise TemplateSyntaxError('Python code blocks not allowed',
+ self.filepath, *pos[1:])
+ try:
+ suite = Suite(data[1], self.filepath, pos[1],
+ lookup=self.lookup)
+ except SyntaxError, err:
+ raise TemplateSyntaxError(err, self.filepath,
+ pos[1] + (err.lineno or 1) - 1,
+ pos[2] + (err.offset or 0))
+ stream.append((EXEC, suite, pos))
+
+ elif kind is COMMENT:
+ if not data.lstrip().startswith('!'):
+ stream.append((kind, data, pos))
+
+ else:
+ stream.append((kind, data, pos))
+
+ return stream
+
+ def _extract_directives(self, stream, namespace, factory):
+ depth = 0
+ dirmap = {} # temporary mapping of directives to elements
+ new_stream = []
+ ns_prefix = {} # namespace prefixes in use
+
+ for kind, data, pos in stream:
+
+ if kind is START:
+ tag, attrs = data
+ directives = []
+ strip = False
+
+ if tag.namespace == namespace:
+ cls = factory.get_directive(tag.localname)
+ if cls is None:
+ raise BadDirectiveError(tag.localname,
+ self.filepath, pos[1])
+ args = dict([(name.localname, value) for name, value
+ in attrs if not name.namespace])
+ directives.append((factory.get_directive_index(cls), cls,
+ args, ns_prefix.copy(), pos))
+ strip = True
+
+ new_attrs = []
+ for name, value in attrs:
+ if name.namespace == namespace:
+ cls = factory.get_directive(name.localname)
+ if cls is None:
+ raise BadDirectiveError(name.localname,
+ self.filepath, pos[1])
+ if type(value) is list and len(value) == 1:
+ value = value[0][1]
+ directives.append((factory.get_directive_index(cls),
+ cls, value, ns_prefix.copy(), pos))
+ else:
+ new_attrs.append((name, value))
+ new_attrs = Attrs(new_attrs)
+
+ if directives:
+ directives.sort()
+ dirmap[(depth, tag)] = (directives, len(new_stream),
+ strip)
+
+ new_stream.append((kind, (tag, new_attrs), pos))
+ depth += 1
+
+ elif kind is END:
+ depth -= 1
+ new_stream.append((kind, data, pos))
+
+ # If there have have directive attributes with the
+ # corresponding start tag, move the events inbetween into
+ # a "subprogram"
+ if (depth, data) in dirmap:
+ directives, offset, strip = dirmap.pop((depth, data))
+ substream = new_stream[offset:]
+ if strip:
+ substream = substream[1:-1]
+ new_stream[offset:] = [
+ (SUB, (directives, substream), pos)
+ ]
+
+ elif kind is SUB:
+ directives, substream = data
+ substream = self._extract_directives(substream, namespace,
+ factory)
+
+ if len(substream) == 1 and substream[0][0] is SUB:
+ added_directives, substream = substream[0][1]
+ directives += added_directives
+
+ new_stream.append((kind, (directives, substream), pos))
+
+ elif kind is START_NS:
+ # Strip out the namespace declaration for template
+ # directives
+ prefix, uri = data
+ ns_prefix[prefix] = uri
+ if uri != namespace:
+ new_stream.append((kind, data, pos))
+
+ elif kind is END_NS:
+ uri = ns_prefix.pop(data, None)
+ if uri and uri != namespace:
+ new_stream.append((kind, data, pos))
+
+ else:
+ new_stream.append((kind, data, pos))
+
+ return new_stream
+
+ def _extract_includes(self, stream):
+ streams = [[]] # stacked lists of events of the "compiled" template
+ prefixes = {}
+ fallbacks = []
+ includes = []
+ xinclude_ns = Namespace(self.XINCLUDE_NAMESPACE)
+
+ for kind, data, pos in stream:
+ stream = streams[-1]
+
+ if kind is START:
+ # Record any directive attributes in start tags
+ tag, attrs = data
+ if tag in xinclude_ns:
+ if tag.localname == 'include':
+ include_href = attrs.get('href')
+ if not include_href:
+ raise TemplateSyntaxError('Include misses required '
+ 'attribute "href"',
+ self.filepath, *pos[1:])
+ includes.append((include_href, attrs.get('parse')))
+ streams.append([])
+ elif tag.localname == 'fallback':
+ streams.append([])
+ fallbacks.append(streams[-1])
+ else:
+ stream.append((kind, (tag, attrs), pos))
+
+ elif kind is END:
+ if fallbacks and data == xinclude_ns['fallback']:
+ assert streams.pop() is fallbacks[-1]
+ elif data == xinclude_ns['include']:
+ fallback = None
+ if len(fallbacks) == len(includes):
+ fallback = fallbacks.pop()
+ streams.pop() # discard anything between the include tags
+ # and the fallback element
+ stream = streams[-1]
+ href, parse = includes.pop()
+ try:
+ cls = {
+ 'xml': MarkupTemplate,
+ 'text': NewTextTemplate
+ }.get(parse) or self.__class__
+ except KeyError:
+ raise TemplateSyntaxError('Invalid value for "parse" '
+ 'attribute of include',
+ self.filepath, *pos[1:])
+ stream.append((INCLUDE, (href, cls, fallback), pos))
+ else:
+ stream.append((kind, data, pos))
+
+ elif kind is START_NS and data[1] == xinclude_ns:
+ # Strip out the XInclude namespace
+ prefixes[data[0]] = data[1]
+
+ elif kind is END_NS and data in prefixes:
+ prefixes.pop(data)
+
+ else:
+ stream.append((kind, data, pos))
+
+ assert len(streams) == 1
+ return streams[0]
+
+ def _interpolate_attrs(self, stream):
+ for kind, data, pos in stream:
+
+ if kind is START:
+ # Record any directive attributes in start tags
+ tag, attrs = data
+ new_attrs = []
+ for name, value in attrs:
+ if value:
+ value = list(interpolate(value, self.filepath, pos[1],
+ pos[2], lookup=self.lookup))
+ if len(value) == 1 and value[0][0] is TEXT:
+ value = value[0][1]
+ new_attrs.append((name, value))
+ data = tag, Attrs(new_attrs)
+
+ yield kind, data, pos
+
+ def _prepare(self, stream):
+ return Template._prepare(self,
+ self._extract_includes(self._interpolate_attrs(stream))
+ )
+
+ def add_directives(self, namespace, factory):
+ """Register a custom `DirectiveFactory` for a given namespace.
+
+ :param namespace: the namespace URI
+ :type namespace: `basestring`
+ :param factory: the directive factory to register
+ :type factory: `DirectiveFactory`
+ :since: version 0.6
+ """
+ assert not self._prepared, 'Too late for adding directives, ' \
+ 'template already prepared'
+ self._stream = self._extract_directives(self._stream, namespace,
+ factory)
+
+ def _match(self, stream, ctxt, start=0, end=None, **vars):
+ """Internal stream filter that applies any defined match templates
+ to the stream.
+ """
+ match_templates = ctxt._match_templates
+
+ tail = []
+ def _strip(stream, append=tail.append):
+ depth = 1
+ next = stream.next
+ while 1:
+ event = next()
+ if event[0] is START:
+ depth += 1
+ elif event[0] is END:
+ depth -= 1
+ if depth > 0:
+ yield event
+ else:
+ append(event)
+ break
+
+ for event in stream:
+
+ # We (currently) only care about start and end events for matching
+ # We might care about namespace events in the future, though
+ if not match_templates or (event[0] is not START and
+ event[0] is not END):
+ yield event
+ continue
+
+ for idx, (test, path, template, hints, namespaces, directives) \
+ in enumerate(match_templates):
+ if idx < start or end is not None and idx >= end:
+ continue
+
+ if test(event, namespaces, ctxt) is True:
+ if 'match_once' in hints:
+ del match_templates[idx]
+ idx -= 1
+
+ # Let the remaining match templates know about the event so
+ # they get a chance to update their internal state
+ for test in [mt[0] for mt in match_templates[idx + 1:]]:
+ test(event, namespaces, ctxt, updateonly=True)
+
+ # Consume and store all events until an end event
+ # corresponding to this start event is encountered
+ pre_end = idx + 1
+ if 'match_once' not in hints and 'not_recursive' in hints:
+ pre_end -= 1
+ inner = _strip(stream)
+ if pre_end > 0:
+ inner = self._match(inner, ctxt, start=start,
+ end=pre_end, **vars)
+ content = self._include(chain([event], inner, tail), ctxt)
+ if 'not_buffered' not in hints:
+ content = list(content)
+ content = Stream(content)
+
+ # Make the select() function available in the body of the
+ # match template
+ selected = [False]
+ def select(path):
+ selected[0] = True
+ return content.select(path, namespaces, ctxt)
+ vars = dict(select=select)
+
+ # Recursively process the output
+ template = _apply_directives(template, directives, ctxt,
+ vars)
+ for event in self._match(self._flatten(template, ctxt,
+ **vars),
+ ctxt, start=idx + 1, **vars):
+ yield event
+
+ # If the match template did not actually call select to
+ # consume the matched stream, the original events need to
+ # be consumed here or they'll get appended to the output
+ if not selected[0]:
+ for event in content:
+ pass
+
+ # Let the remaining match templates know about the last
+ # event in the matched content, so they can update their
+ # internal state accordingly
+ for test in [mt[0] for mt in match_templates[idx + 1:]]:
+ test(tail[0], namespaces, ctxt, updateonly=True)
+
+ break
+
+ else: # no matches
+ yield event
diff --git a/websdk/genshi/template/plugin.py b/websdk/genshi/template/plugin.py
new file mode 100644
index 0000000..70d56af
--- /dev/null
+++ b/websdk/genshi/template/plugin.py
@@ -0,0 +1,176 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# Copyright (C) 2006 Matthew Good
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Basic support for the template engine plugin API used by TurboGears and
+CherryPy/Buffet.
+"""
+
+from genshi.input import ET, HTML, XML
+from genshi.output import DocType
+from genshi.template.base import Template
+from genshi.template.loader import TemplateLoader
+from genshi.template.markup import MarkupTemplate
+from genshi.template.text import TextTemplate, NewTextTemplate
+
+__all__ = ['ConfigurationError', 'AbstractTemplateEnginePlugin',
+ 'MarkupTemplateEnginePlugin', 'TextTemplateEnginePlugin']
+__docformat__ = 'restructuredtext en'
+
+
+class ConfigurationError(ValueError):
+ """Exception raised when invalid plugin options are encountered."""
+
+
+class AbstractTemplateEnginePlugin(object):
+ """Implementation of the plugin API."""
+
+ template_class = None
+ extension = None
+
+ def __init__(self, extra_vars_func=None, options=None):
+ self.get_extra_vars = extra_vars_func
+ if options is None:
+ options = {}
+ self.options = options
+
+ self.default_encoding = options.get('genshi.default_encoding', 'utf-8')
+ auto_reload = options.get('genshi.auto_reload', '1')
+ if isinstance(auto_reload, basestring):
+ auto_reload = auto_reload.lower() in ('1', 'on', 'yes', 'true')
+ search_path = [p for p in
+ options.get('genshi.search_path', '').split(':') if p]
+ self.use_package_naming = not search_path
+ try:
+ max_cache_size = int(options.get('genshi.max_cache_size', 25))
+ except ValueError:
+ raise ConfigurationError('Invalid value for max_cache_size: "%s"' %
+ options.get('genshi.max_cache_size'))
+
+ loader_callback = options.get('genshi.loader_callback', None)
+ if loader_callback and not hasattr(loader_callback, '__call__'):
+ raise ConfigurationError('loader callback must be a function')
+
+ lookup_errors = options.get('genshi.lookup_errors', 'strict')
+ if lookup_errors not in ('lenient', 'strict'):
+ raise ConfigurationError('Unknown lookup errors mode "%s"' %
+ lookup_errors)
+
+ try:
+ allow_exec = bool(options.get('genshi.allow_exec', True))
+ except ValueError:
+ raise ConfigurationError('Invalid value for allow_exec "%s"' %
+ options.get('genshi.allow_exec'))
+
+ self.loader = TemplateLoader([p for p in search_path if p],
+ auto_reload=auto_reload,
+ max_cache_size=max_cache_size,
+ default_class=self.template_class,
+ variable_lookup=lookup_errors,
+ allow_exec=allow_exec,
+ callback=loader_callback)
+
+ def load_template(self, templatename, template_string=None):
+ """Find a template specified in python 'dot' notation, or load one from
+ a string.
+ """
+ if template_string is not None:
+ return self.template_class(template_string)
+
+ if self.use_package_naming:
+ divider = templatename.rfind('.')
+ if divider >= 0:
+ from pkg_resources import resource_filename
+ package = templatename[:divider]
+ basename = templatename[divider + 1:] + self.extension
+ templatename = resource_filename(package, basename)
+
+ return self.loader.load(templatename)
+
+ def _get_render_options(self, format=None, fragment=False):
+ if format is None:
+ format = self.default_format
+ kwargs = {'method': format}
+ if self.default_encoding:
+ kwargs['encoding'] = self.default_encoding
+ return kwargs
+
+ def render(self, info, format=None, fragment=False, template=None):
+ """Render the template to a string using the provided info."""
+ kwargs = self._get_render_options(format=format, fragment=fragment)
+ return self.transform(info, template).render(**kwargs)
+
+ def transform(self, info, template):
+ """Render the output to an event stream."""
+ if not isinstance(template, Template):
+ template = self.load_template(template)
+ return template.generate(**info)
+
+
+class MarkupTemplateEnginePlugin(AbstractTemplateEnginePlugin):
+ """Implementation of the plugin API for markup templates."""
+
+ template_class = MarkupTemplate
+ extension = '.html'
+
+ def __init__(self, extra_vars_func=None, options=None):
+ AbstractTemplateEnginePlugin.__init__(self, extra_vars_func, options)
+
+ default_doctype = self.options.get('genshi.default_doctype')
+ if default_doctype:
+ doctype = DocType.get(default_doctype)
+ if doctype is None:
+ raise ConfigurationError('Unknown doctype %r' % default_doctype)
+ self.default_doctype = doctype
+ else:
+ self.default_doctype = None
+
+ format = self.options.get('genshi.default_format', 'html').lower()
+ if format not in ('html', 'xhtml', 'xml', 'text'):
+ raise ConfigurationError('Unknown output format %r' % format)
+ self.default_format = format
+
+ def _get_render_options(self, format=None, fragment=False):
+ kwargs = super(MarkupTemplateEnginePlugin,
+ self)._get_render_options(format, fragment)
+ if self.default_doctype and not fragment:
+ kwargs['doctype'] = self.default_doctype
+ return kwargs
+
+ def transform(self, info, template):
+ """Render the output to an event stream."""
+ data = {'ET': ET, 'HTML': HTML, 'XML': XML}
+ if self.get_extra_vars:
+ data.update(self.get_extra_vars())
+ data.update(info)
+ return super(MarkupTemplateEnginePlugin, self).transform(data, template)
+
+
+class TextTemplateEnginePlugin(AbstractTemplateEnginePlugin):
+ """Implementation of the plugin API for text templates."""
+
+ template_class = TextTemplate
+ extension = '.txt'
+ default_format = 'text'
+
+ def __init__(self, extra_vars_func=None, options=None):
+ if options is None:
+ options = {}
+
+ new_syntax = options.get('genshi.new_text_syntax')
+ if isinstance(new_syntax, basestring):
+ new_syntax = new_syntax.lower() in ('1', 'on', 'yes', 'true')
+ if new_syntax:
+ self.template_class = NewTextTemplate
+
+ AbstractTemplateEnginePlugin.__init__(self, extra_vars_func, options)
diff --git a/websdk/genshi/template/text.py b/websdk/genshi/template/text.py
new file mode 100644
index 0000000..746226c
--- /dev/null
+++ b/websdk/genshi/template/text.py
@@ -0,0 +1,333 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Plain text templating engine.
+
+This module implements two template language syntaxes, at least for a certain
+transitional period. `OldTextTemplate` (aliased to just `TextTemplate`) defines
+a syntax that was inspired by Cheetah/Velocity. `NewTextTemplate` on the other
+hand is inspired by the syntax of the Django template language, which has more
+explicit delimiting of directives, and is more flexible with regards to
+white space and line breaks.
+
+In a future release, `OldTextTemplate` will be phased out in favor of
+`NewTextTemplate`, as the names imply. Therefore the new syntax is strongly
+recommended for new projects, and existing projects may want to migrate to the
+new syntax to remain compatible with future Genshi releases.
+"""
+
+import re
+
+from genshi.core import TEXT
+from genshi.template.base import BadDirectiveError, Template, \
+ TemplateSyntaxError, EXEC, INCLUDE, SUB
+from genshi.template.eval import Suite
+from genshi.template.directives import *
+from genshi.template.directives import Directive
+from genshi.template.interpolation import interpolate
+
+__all__ = ['NewTextTemplate', 'OldTextTemplate', 'TextTemplate']
+__docformat__ = 'restructuredtext en'
+
+
+class NewTextTemplate(Template):
+ r"""Implementation of a simple text-based template engine. This class will
+ replace `OldTextTemplate` in a future release.
+
+ It uses a more explicit delimiting style for directives: instead of the old
+ style which required putting directives on separate lines that were prefixed
+ with a ``#`` sign, directives and commenbtsr are enclosed in delimiter pairs
+ (by default ``{% ... %}`` and ``{# ... #}``, respectively).
+
+ Variable substitution uses the same interpolation syntax as for markup
+ languages: simple references are prefixed with a dollar sign, more complex
+ expression enclosed in curly braces.
+
+ >>> tmpl = NewTextTemplate('''Dear $name,
+ ...
+ ... {# This is a comment #}
+ ... We have the following items for you:
+ ... {% for item in items %}
+ ... * ${'Item %d' % item}
+ ... {% end %}
+ ... ''')
+ >>> print(tmpl.generate(name='Joe', items=[1, 2, 3]).render(encoding=None))
+ Dear Joe,
+ <BLANKLINE>
+ <BLANKLINE>
+ We have the following items for you:
+ <BLANKLINE>
+ * Item 1
+ <BLANKLINE>
+ * Item 2
+ <BLANKLINE>
+ * Item 3
+ <BLANKLINE>
+ <BLANKLINE>
+
+ By default, no spaces or line breaks are removed. If a line break should
+ not be included in the output, prefix it with a backslash:
+
+ >>> tmpl = NewTextTemplate('''Dear $name,
+ ...
+ ... {# This is a comment #}\
+ ... We have the following items for you:
+ ... {% for item in items %}\
+ ... * $item
+ ... {% end %}\
+ ... ''')
+ >>> print(tmpl.generate(name='Joe', items=[1, 2, 3]).render(encoding=None))
+ Dear Joe,
+ <BLANKLINE>
+ We have the following items for you:
+ * 1
+ * 2
+ * 3
+ <BLANKLINE>
+
+ Backslashes are also used to escape the start delimiter of directives and
+ comments:
+
+ >>> tmpl = NewTextTemplate('''Dear $name,
+ ...
+ ... \{# This is a comment #}
+ ... We have the following items for you:
+ ... {% for item in items %}\
+ ... * $item
+ ... {% end %}\
+ ... ''')
+ >>> print(tmpl.generate(name='Joe', items=[1, 2, 3]).render(encoding=None))
+ Dear Joe,
+ <BLANKLINE>
+ {# This is a comment #}
+ We have the following items for you:
+ * 1
+ * 2
+ * 3
+ <BLANKLINE>
+
+ :since: version 0.5
+ """
+ directives = [('def', DefDirective),
+ ('when', WhenDirective),
+ ('otherwise', OtherwiseDirective),
+ ('for', ForDirective),
+ ('if', IfDirective),
+ ('choose', ChooseDirective),
+ ('with', WithDirective)]
+ serializer = 'text'
+
+ _DIRECTIVE_RE = r'((?<!\\)%s\s*(\w+)\s*(.*?)\s*%s|(?<!\\)%s.*?%s)'
+ _ESCAPE_RE = r'\\\n|\\(\\)|\\(%s)|\\(%s)'
+
+ def __init__(self, source, filepath=None, filename=None, loader=None,
+ encoding=None, lookup='strict', allow_exec=False,
+ delims=('{%', '%}', '{#', '#}')):
+ self.delimiters = delims
+ Template.__init__(self, source, filepath=filepath, filename=filename,
+ loader=loader, encoding=encoding, lookup=lookup)
+
+ def _get_delims(self):
+ return self._delims
+ def _set_delims(self, delims):
+ if len(delims) != 4:
+ raise ValueError('delimiers tuple must have exactly four elements')
+ self._delims = delims
+ self._directive_re = re.compile(self._DIRECTIVE_RE % tuple(
+ [re.escape(d) for d in delims]
+ ), re.DOTALL)
+ self._escape_re = re.compile(self._ESCAPE_RE % tuple(
+ [re.escape(d) for d in delims[::2]]
+ ))
+ delimiters = property(_get_delims, _set_delims, """\
+ The delimiters for directives and comments. This should be a four item tuple
+ of the form ``(directive_start, directive_end, comment_start,
+ comment_end)``, where each item is a string.
+ """)
+
+ def _parse(self, source, encoding):
+ """Parse the template from text input."""
+ stream = [] # list of events of the "compiled" template
+ dirmap = {} # temporary mapping of directives to elements
+ depth = 0
+
+ source = source.read()
+ if isinstance(source, str):
+ source = source.decode(encoding or 'utf-8', 'replace')
+ offset = 0
+ lineno = 1
+
+ _escape_sub = self._escape_re.sub
+ def _escape_repl(mo):
+ groups = [g for g in mo.groups() if g]
+ if not groups:
+ return ''
+ return groups[0]
+
+ for idx, mo in enumerate(self._directive_re.finditer(source)):
+ start, end = mo.span(1)
+ if start > offset:
+ text = _escape_sub(_escape_repl, source[offset:start])
+ for kind, data, pos in interpolate(text, self.filepath, lineno,
+ lookup=self.lookup):
+ stream.append((kind, data, pos))
+ lineno += len(text.splitlines())
+
+ lineno += len(source[start:end].splitlines())
+ command, value = mo.group(2, 3)
+
+ if command == 'include':
+ pos = (self.filename, lineno, 0)
+ value = list(interpolate(value, self.filepath, lineno, 0,
+ lookup=self.lookup))
+ if len(value) == 1 and value[0][0] is TEXT:
+ value = value[0][1]
+ stream.append((INCLUDE, (value, None, []), pos))
+
+ elif command == 'python':
+ if not self.allow_exec:
+ raise TemplateSyntaxError('Python code blocks not allowed',
+ self.filepath, lineno)
+ try:
+ suite = Suite(value, self.filepath, lineno,
+ lookup=self.lookup)
+ except SyntaxError, err:
+ raise TemplateSyntaxError(err, self.filepath,
+ lineno + (err.lineno or 1) - 1)
+ pos = (self.filename, lineno, 0)
+ stream.append((EXEC, suite, pos))
+
+ elif command == 'end':
+ depth -= 1
+ if depth in dirmap:
+ directive, start_offset = dirmap.pop(depth)
+ substream = stream[start_offset:]
+ stream[start_offset:] = [(SUB, ([directive], substream),
+ (self.filepath, lineno, 0))]
+
+ elif command:
+ cls = self.get_directive(command)
+ if cls is None:
+ raise BadDirectiveError(command)
+ directive = 0, cls, value, None, (self.filepath, lineno, 0)
+ dirmap[depth] = (directive, len(stream))
+ depth += 1
+
+ offset = end
+
+ if offset < len(source):
+ text = _escape_sub(_escape_repl, source[offset:])
+ for kind, data, pos in interpolate(text, self.filepath, lineno,
+ lookup=self.lookup):
+ stream.append((kind, data, pos))
+
+ return stream
+
+
+class OldTextTemplate(Template):
+ """Legacy implementation of the old syntax text-based templates. This class
+ is provided in a transition phase for backwards compatibility. New code
+ should use the `NewTextTemplate` class and the improved syntax it provides.
+
+ >>> tmpl = OldTextTemplate('''Dear $name,
+ ...
+ ... We have the following items for you:
+ ... #for item in items
+ ... * $item
+ ... #end
+ ...
+ ... All the best,
+ ... Foobar''')
+ >>> print(tmpl.generate(name='Joe', items=[1, 2, 3]).render(encoding=None))
+ Dear Joe,
+ <BLANKLINE>
+ We have the following items for you:
+ * 1
+ * 2
+ * 3
+ <BLANKLINE>
+ All the best,
+ Foobar
+ """
+ directives = [('def', DefDirective),
+ ('when', WhenDirective),
+ ('otherwise', OtherwiseDirective),
+ ('for', ForDirective),
+ ('if', IfDirective),
+ ('choose', ChooseDirective),
+ ('with', WithDirective)]
+ serializer = 'text'
+
+ _DIRECTIVE_RE = re.compile(r'(?:^[ \t]*(?<!\\)#(end).*\n?)|'
+ r'(?:^[ \t]*(?<!\\)#((?:\w+|#).*)\n?)',
+ re.MULTILINE)
+
+ def _parse(self, source, encoding):
+ """Parse the template from text input."""
+ stream = [] # list of events of the "compiled" template
+ dirmap = {} # temporary mapping of directives to elements
+ depth = 0
+
+ source = source.read()
+ if isinstance(source, str):
+ source = source.decode(encoding or 'utf-8', 'replace')
+ offset = 0
+ lineno = 1
+
+ for idx, mo in enumerate(self._DIRECTIVE_RE.finditer(source)):
+ start, end = mo.span()
+ if start > offset:
+ text = source[offset:start]
+ for kind, data, pos in interpolate(text, self.filepath, lineno,
+ lookup=self.lookup):
+ stream.append((kind, data, pos))
+ lineno += len(text.splitlines())
+
+ text = source[start:end].lstrip()[1:]
+ lineno += len(text.splitlines())
+ directive = text.split(None, 1)
+ if len(directive) > 1:
+ command, value = directive
+ else:
+ command, value = directive[0], None
+
+ if command == 'end':
+ depth -= 1
+ if depth in dirmap:
+ directive, start_offset = dirmap.pop(depth)
+ substream = stream[start_offset:]
+ stream[start_offset:] = [(SUB, ([directive], substream),
+ (self.filepath, lineno, 0))]
+ elif command == 'include':
+ pos = (self.filename, lineno, 0)
+ stream.append((INCLUDE, (value.strip(), None, []), pos))
+ elif command != '#':
+ cls = self.get_directive(command)
+ if cls is None:
+ raise BadDirectiveError(command)
+ directive = 0, cls, value, None, (self.filepath, lineno, 0)
+ dirmap[depth] = (directive, len(stream))
+ depth += 1
+
+ offset = end
+
+ if offset < len(source):
+ text = source[offset:].replace('\\#', '#')
+ for kind, data, pos in interpolate(text, self.filepath, lineno,
+ lookup=self.lookup):
+ stream.append((kind, data, pos))
+
+ return stream
+
+
+TextTemplate = OldTextTemplate
diff --git a/websdk/genshi/util.py b/websdk/genshi/util.py
new file mode 100644
index 0000000..b964a01
--- /dev/null
+++ b/websdk/genshi/util.py
@@ -0,0 +1,274 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006-2009 Edgewall Software
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://genshi.edgewall.org/wiki/License.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For the exact contribution history, see the revision
+# history and logs, available at http://genshi.edgewall.org/log/.
+
+"""Various utility classes and functions."""
+
+import htmlentitydefs as entities
+import re
+
+__docformat__ = 'restructuredtext en'
+
+
+class LRUCache(dict):
+ """A dictionary-like object that stores only a certain number of items, and
+ discards its least recently used item when full.
+
+ >>> cache = LRUCache(3)
+ >>> cache['A'] = 0
+ >>> cache['B'] = 1
+ >>> cache['C'] = 2
+ >>> len(cache)
+ 3
+
+ >>> cache['A']
+ 0
+
+ Adding new items to the cache does not increase its size. Instead, the least
+ recently used item is dropped:
+
+ >>> cache['D'] = 3
+ >>> len(cache)
+ 3
+ >>> 'B' in cache
+ False
+
+ Iterating over the cache returns the keys, starting with the most recently
+ used:
+
+ >>> for key in cache:
+ ... print(key)
+ D
+ A
+ C
+
+ This code is based on the LRUCache class from ``myghtyutils.util``, written
+ by Mike Bayer and released under the MIT license. See:
+
+ http://svn.myghty.org/myghtyutils/trunk/lib/myghtyutils/util.py
+ """
+
+ class _Item(object):
+ def __init__(self, key, value):
+ self.prv = self.nxt = None
+ self.key = key
+ self.value = value
+ def __repr__(self):
+ return repr(self.value)
+
+ def __init__(self, capacity):
+ self._dict = dict()
+ self.capacity = capacity
+ self.head = None
+ self.tail = None
+
+ def __contains__(self, key):
+ return key in self._dict
+
+ def __iter__(self):
+ cur = self.head
+ while cur:
+ yield cur.key
+ cur = cur.nxt
+
+ def __len__(self):
+ return len(self._dict)
+
+ def __getitem__(self, key):
+ item = self._dict[key]
+ self._update_item(item)
+ return item.value
+
+ def __setitem__(self, key, value):
+ item = self._dict.get(key)
+ if item is None:
+ item = self._Item(key, value)
+ self._dict[key] = item
+ self._insert_item(item)
+ else:
+ item.value = value
+ self._update_item(item)
+ self._manage_size()
+
+ def __repr__(self):
+ return repr(self._dict)
+
+ def _insert_item(self, item):
+ item.prv = None
+ item.nxt = self.head
+ if self.head is not None:
+ self.head.prv = item
+ else:
+ self.tail = item
+ self.head = item
+ self._manage_size()
+
+ def _manage_size(self):
+ while len(self._dict) > self.capacity:
+ olditem = self._dict[self.tail.key]
+ del self._dict[self.tail.key]
+ if self.tail != self.head:
+ self.tail = self.tail.prv
+ self.tail.nxt = None
+ else:
+ self.head = self.tail = None
+
+ def _update_item(self, item):
+ if self.head == item:
+ return
+
+ prv = item.prv
+ prv.nxt = item.nxt
+ if item.nxt is not None:
+ item.nxt.prv = prv
+ else:
+ self.tail = prv
+
+ item.prv = None
+ item.nxt = self.head
+ self.head.prv = self.head = item
+
+
+def flatten(items):
+ """Flattens a potentially nested sequence into a flat list.
+
+ :param items: the sequence to flatten
+
+ >>> flatten((1, 2))
+ [1, 2]
+ >>> flatten([1, (2, 3), 4])
+ [1, 2, 3, 4]
+ >>> flatten([1, (2, [3, 4]), 5])
+ [1, 2, 3, 4, 5]
+ """
+ retval = []
+ for item in items:
+ if isinstance(item, (frozenset, list, set, tuple)):
+ retval += flatten(item)
+ else:
+ retval.append(item)
+ return retval
+
+
+def plaintext(text, keeplinebreaks=True):
+ """Return the text with all entities and tags removed.
+
+ >>> plaintext('<b>1 &lt; 2</b>')
+ u'1 < 2'
+
+ The `keeplinebreaks` parameter can be set to ``False`` to replace any line
+ breaks by simple spaces:
+
+ >>> plaintext('''<b>1
+ ... &lt;
+ ... 2</b>''', keeplinebreaks=False)
+ u'1 < 2'
+
+ :param text: the text to convert to plain text
+ :param keeplinebreaks: whether line breaks in the text should be kept intact
+ :return: the text with tags and entities removed
+ """
+ text = stripentities(striptags(text))
+ if not keeplinebreaks:
+ text = text.replace('\n', ' ')
+ return text
+
+
+_STRIPENTITIES_RE = re.compile(r'&(?:#((?:\d+)|(?:[xX][0-9a-fA-F]+));?|(\w+);)')
+def stripentities(text, keepxmlentities=False):
+ """Return a copy of the given text with any character or numeric entities
+ replaced by the equivalent UTF-8 characters.
+
+ >>> stripentities('1 &lt; 2')
+ u'1 < 2'
+ >>> stripentities('more &hellip;')
+ u'more \u2026'
+ >>> stripentities('&#8230;')
+ u'\u2026'
+ >>> stripentities('&#x2026;')
+ u'\u2026'
+
+ If the `keepxmlentities` parameter is provided and is a truth value, the
+ core XML entities (&amp;, &apos;, &gt;, &lt; and &quot;) are left intact.
+
+ >>> stripentities('1 &lt; 2 &hellip;', keepxmlentities=True)
+ u'1 &lt; 2 \u2026'
+ """
+ def _replace_entity(match):
+ if match.group(1): # numeric entity
+ ref = match.group(1)
+ if ref.startswith('x'):
+ ref = int(ref[1:], 16)
+ else:
+ ref = int(ref, 10)
+ return unichr(ref)
+ else: # character entity
+ ref = match.group(2)
+ if keepxmlentities and ref in ('amp', 'apos', 'gt', 'lt', 'quot'):
+ return '&%s;' % ref
+ try:
+ return unichr(entities.name2codepoint[ref])
+ except KeyError:
+ if keepxmlentities:
+ return '&amp;%s;' % ref
+ else:
+ return ref
+ return _STRIPENTITIES_RE.sub(_replace_entity, text)
+
+
+_STRIPTAGS_RE = re.compile(r'(<!--.*?-->|<[^>]*>)')
+def striptags(text):
+ """Return a copy of the text with any XML/HTML tags removed.
+
+ >>> striptags('<span>Foo</span> bar')
+ 'Foo bar'
+ >>> striptags('<span class="bar">Foo</span>')
+ 'Foo'
+ >>> striptags('Foo<br />')
+ 'Foo'
+
+ HTML/XML comments are stripped, too:
+
+ >>> striptags('<!-- <blub>hehe</blah> -->test')
+ 'test'
+
+ :param text: the string to remove tags from
+ :return: the text with tags removed
+ """
+ return _STRIPTAGS_RE.sub('', text)
+
+
+def stringrepr(string):
+ ascii = string.encode('ascii', 'backslashreplace')
+ quoted = "'" + ascii.replace("'", "\\'") + "'"
+ if len(ascii) > len(string):
+ return 'u' + quoted
+ return quoted
+
+
+# Compatibility fallback implementations for older Python versions
+
+try:
+ all = all
+ any = any
+except NameError:
+ def any(S):
+ for x in S:
+ if x:
+ return True
+ return False
+
+ def all(S):
+ for x in S:
+ if not x:
+ return False
+ return True
diff --git a/websdk/jinja2/__init__.py b/websdk/jinja2/__init__.py
new file mode 100644
index 0000000..5b349d6
--- /dev/null
+++ b/websdk/jinja2/__init__.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2
+ ~~~~~~
+
+ Jinja2 is a template engine written in pure Python. It provides a
+ Django inspired non-XML syntax but supports inline expressions and
+ an optional sandboxed environment.
+
+ Nutshell
+ --------
+
+ Here a small example of a Jinja2 template::
+
+ {% extends 'base.html' %}
+ {% block title %}Memberlist{% endblock %}
+ {% block content %}
+ <ul>
+ {% for user in users %}
+ <li><a href="{{ user.url }}">{{ user.username }}</a></li>
+ {% endfor %}
+ </ul>
+ {% endblock %}
+
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+__docformat__ = 'restructuredtext en'
+__version__ = '2.7-dev'
+
+# high level interface
+from jinja2.environment import Environment, Template
+
+# loaders
+from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \
+ DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \
+ ModuleLoader
+
+# bytecode caches
+from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \
+ MemcachedBytecodeCache
+
+# undefined types
+from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined
+
+# exceptions
+from jinja2.exceptions import TemplateError, UndefinedError, \
+ TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \
+ TemplateAssertionError
+
+# decorators and public utilities
+from jinja2.filters import environmentfilter, contextfilter, \
+ evalcontextfilter
+from jinja2.utils import Markup, escape, clear_caches, \
+ environmentfunction, evalcontextfunction, contextfunction, \
+ is_undefined
+
+__all__ = [
+ 'Environment', 'Template', 'BaseLoader', 'FileSystemLoader',
+ 'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader',
+ 'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache',
+ 'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined',
+ 'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound',
+ 'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError',
+ 'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape',
+ 'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined',
+ 'evalcontextfilter', 'evalcontextfunction'
+]
diff --git a/websdk/jinja2/_debugsupport.c b/websdk/jinja2/_debugsupport.c
new file mode 100644
index 0000000..e756d8e
--- /dev/null
+++ b/websdk/jinja2/_debugsupport.c
@@ -0,0 +1,78 @@
+/**
+ * jinja2._debugsupport
+ * ~~~~~~~~~~~~~~~~~~~~
+ *
+ * C implementation of `tb_set_next`.
+ *
+ * :copyright: (c) 2010 by the Jinja Team.
+ * :license: BSD.
+ */
+
+#include <Python.h>
+
+
+static PyObject*
+tb_set_next(PyObject *self, PyObject *args)
+{
+ PyTracebackObject *tb, *old;
+ PyObject *next;
+
+ if (!PyArg_ParseTuple(args, "O!O:tb_set_next", &PyTraceBack_Type, &tb, &next))
+ return NULL;
+ if (next == Py_None)
+ next = NULL;
+ else if (!PyTraceBack_Check(next)) {
+ PyErr_SetString(PyExc_TypeError,
+ "tb_set_next arg 2 must be traceback or None");
+ return NULL;
+ }
+ else
+ Py_INCREF(next);
+
+ old = tb->tb_next;
+ tb->tb_next = (PyTracebackObject*)next;
+ Py_XDECREF(old);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef module_methods[] = {
+ {"tb_set_next", (PyCFunction)tb_set_next, METH_VARARGS,
+ "Set the tb_next member of a traceback object."},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+
+#if PY_MAJOR_VERSION < 3
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+init_debugsupport(void)
+{
+ Py_InitModule3("jinja2._debugsupport", module_methods, "");
+}
+
+#else /* Python 3.x module initialization */
+
+static struct PyModuleDef module_definition = {
+ PyModuleDef_HEAD_INIT,
+ "jinja2._debugsupport",
+ NULL,
+ -1,
+ module_methods,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+PyMODINIT_FUNC
+PyInit__debugsupport(void)
+{
+ return PyModule_Create(&module_definition);
+}
+
+#endif
diff --git a/websdk/jinja2/_markupsafe/__init__.py b/websdk/jinja2/_markupsafe/__init__.py
new file mode 100644
index 0000000..ec7bd57
--- /dev/null
+++ b/websdk/jinja2/_markupsafe/__init__.py
@@ -0,0 +1,225 @@
+# -*- coding: utf-8 -*-
+"""
+ markupsafe
+ ~~~~~~~~~~
+
+ Implements a Markup string.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+from itertools import imap
+
+
+__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
+
+
+_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
+_entity_re = re.compile(r'&([^;]+);')
+
+
+class Markup(unicode):
+ r"""Marks a string as being safe for inclusion in HTML/XML output without
+ needing to be escaped. This implements the `__html__` interface a couple
+ of frameworks and web applications use. :class:`Markup` is a direct
+ subclass of `unicode` and provides all the methods of `unicode` just that
+ it escapes arguments passed and always returns `Markup`.
+
+ The `escape` function returns markup objects so that double escaping can't
+ happen.
+
+ The constructor of the :class:`Markup` class can be used for three
+ different things: When passed an unicode object it's assumed to be safe,
+ when passed an object with an HTML representation (has an `__html__`
+ method) that representation is used, otherwise the object passed is
+ converted into a unicode string and then assumed to be safe:
+
+ >>> Markup("Hello <em>World</em>!")
+ Markup(u'Hello <em>World</em>!')
+ >>> class Foo(object):
+ ... def __html__(self):
+ ... return '<a href="#">foo</a>'
+ ...
+ >>> Markup(Foo())
+ Markup(u'<a href="#">foo</a>')
+
+ If you want object passed being always treated as unsafe you can use the
+ :meth:`escape` classmethod to create a :class:`Markup` object:
+
+ >>> Markup.escape("Hello <em>World</em>!")
+ Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
+
+ Operations on a markup string are markup aware which means that all
+ arguments are passed through the :func:`escape` function:
+
+ >>> em = Markup("<em>%s</em>")
+ >>> em % "foo & bar"
+ Markup(u'<em>foo &amp; bar</em>')
+ >>> strong = Markup("<strong>%(text)s</strong>")
+ >>> strong % {'text': '<blink>hacker here</blink>'}
+ Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
+ >>> Markup("<em>Hello</em> ") + "<foo>"
+ Markup(u'<em>Hello</em> &lt;foo&gt;')
+ """
+ __slots__ = ()
+
+ def __new__(cls, base=u'', encoding=None, errors='strict'):
+ if hasattr(base, '__html__'):
+ base = base.__html__()
+ if encoding is None:
+ return unicode.__new__(cls, base)
+ return unicode.__new__(cls, base, encoding, errors)
+
+ def __html__(self):
+ return self
+
+ def __add__(self, other):
+ if hasattr(other, '__html__') or isinstance(other, basestring):
+ return self.__class__(unicode(self) + unicode(escape(other)))
+ return NotImplemented
+
+ def __radd__(self, other):
+ if hasattr(other, '__html__') or isinstance(other, basestring):
+ return self.__class__(unicode(escape(other)) + unicode(self))
+ return NotImplemented
+
+ def __mul__(self, num):
+ if isinstance(num, (int, long)):
+ return self.__class__(unicode.__mul__(self, num))
+ return NotImplemented
+ __rmul__ = __mul__
+
+ def __mod__(self, arg):
+ if isinstance(arg, tuple):
+ arg = tuple(imap(_MarkupEscapeHelper, arg))
+ else:
+ arg = _MarkupEscapeHelper(arg)
+ return self.__class__(unicode.__mod__(self, arg))
+
+ def __repr__(self):
+ return '%s(%s)' % (
+ self.__class__.__name__,
+ unicode.__repr__(self)
+ )
+
+ def join(self, seq):
+ return self.__class__(unicode.join(self, imap(escape, seq)))
+ join.__doc__ = unicode.join.__doc__
+
+ def split(self, *args, **kwargs):
+ return map(self.__class__, unicode.split(self, *args, **kwargs))
+ split.__doc__ = unicode.split.__doc__
+
+ def rsplit(self, *args, **kwargs):
+ return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
+ rsplit.__doc__ = unicode.rsplit.__doc__
+
+ def splitlines(self, *args, **kwargs):
+ return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
+ splitlines.__doc__ = unicode.splitlines.__doc__
+
+ def unescape(self):
+ r"""Unescape markup again into an unicode string. This also resolves
+ known HTML4 and XHTML entities:
+
+ >>> Markup("Main &raquo; <em>About</em>").unescape()
+ u'Main \xbb <em>About</em>'
+ """
+ from jinja2._markupsafe._constants import HTML_ENTITIES
+ def handle_match(m):
+ name = m.group(1)
+ if name in HTML_ENTITIES:
+ return unichr(HTML_ENTITIES[name])
+ try:
+ if name[:2] in ('#x', '#X'):
+ return unichr(int(name[2:], 16))
+ elif name.startswith('#'):
+ return unichr(int(name[1:]))
+ except ValueError:
+ pass
+ return u''
+ return _entity_re.sub(handle_match, unicode(self))
+
+ def striptags(self):
+ r"""Unescape markup into an unicode string and strip all tags. This
+ also resolves known HTML4 and XHTML entities. Whitespace is
+ normalized to one:
+
+ >>> Markup("Main &raquo; <em>About</em>").striptags()
+ u'Main \xbb About'
+ """
+ stripped = u' '.join(_striptags_re.sub('', self).split())
+ return Markup(stripped).unescape()
+
+ @classmethod
+ def escape(cls, s):
+ """Escape the string. Works like :func:`escape` with the difference
+ that for subclasses of :class:`Markup` this function would return the
+ correct subclass.
+ """
+ rv = escape(s)
+ if rv.__class__ is not cls:
+ return cls(rv)
+ return rv
+
+ def make_wrapper(name):
+ orig = getattr(unicode, name)
+ def func(self, *args, **kwargs):
+ args = _escape_argspec(list(args), enumerate(args))
+ _escape_argspec(kwargs, kwargs.iteritems())
+ return self.__class__(orig(self, *args, **kwargs))
+ func.__name__ = orig.__name__
+ func.__doc__ = orig.__doc__
+ return func
+
+ for method in '__getitem__', 'capitalize', \
+ 'title', 'lower', 'upper', 'replace', 'ljust', \
+ 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
+ 'translate', 'expandtabs', 'swapcase', 'zfill':
+ locals()[method] = make_wrapper(method)
+
+ # new in python 2.5
+ if hasattr(unicode, 'partition'):
+ partition = make_wrapper('partition'),
+ rpartition = make_wrapper('rpartition')
+
+ # new in python 2.6
+ if hasattr(unicode, 'format'):
+ format = make_wrapper('format')
+
+ # not in python 3
+ if hasattr(unicode, '__getslice__'):
+ __getslice__ = make_wrapper('__getslice__')
+
+ del method, make_wrapper
+
+
+def _escape_argspec(obj, iterable):
+ """Helper for various string-wrapped functions."""
+ for key, value in iterable:
+ if hasattr(value, '__html__') or isinstance(value, basestring):
+ obj[key] = escape(value)
+ return obj
+
+
+class _MarkupEscapeHelper(object):
+ """Helper for Markup.__mod__"""
+
+ def __init__(self, obj):
+ self.obj = obj
+
+ __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
+ __str__ = lambda s: str(escape(s.obj))
+ __unicode__ = lambda s: unicode(escape(s.obj))
+ __repr__ = lambda s: str(escape(repr(s.obj)))
+ __int__ = lambda s: int(s.obj)
+ __float__ = lambda s: float(s.obj)
+
+
+# we have to import it down here as the speedups and native
+# modules imports the markup type which is define above.
+try:
+ from jinja2._markupsafe._speedups import escape, escape_silent, soft_unicode
+except ImportError:
+ from jinja2._markupsafe._native import escape, escape_silent, soft_unicode
diff --git a/websdk/jinja2/_markupsafe/_bundle.py b/websdk/jinja2/_markupsafe/_bundle.py
new file mode 100644
index 0000000..e694faf
--- /dev/null
+++ b/websdk/jinja2/_markupsafe/_bundle.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2._markupsafe._bundle
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ This script pulls in markupsafe from a source folder and
+ bundles it with Jinja2. It does not pull in the speedups
+ module though.
+
+ :copyright: Copyright 2010 by the Jinja team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+import sys
+import os
+import re
+
+
+def rewrite_imports(lines):
+ for idx, line in enumerate(lines):
+ new_line = re.sub(r'(import|from)\s+markupsafe\b',
+ r'\1 jinja2._markupsafe', line)
+ if new_line != line:
+ lines[idx] = new_line
+
+
+def main():
+ if len(sys.argv) != 2:
+ print 'error: only argument is path to markupsafe'
+ sys.exit(1)
+ basedir = os.path.dirname(__file__)
+ markupdir = sys.argv[1]
+ for filename in os.listdir(markupdir):
+ if filename.endswith('.py'):
+ f = open(os.path.join(markupdir, filename))
+ try:
+ lines = list(f)
+ finally:
+ f.close()
+ rewrite_imports(lines)
+ f = open(os.path.join(basedir, filename), 'w')
+ try:
+ for line in lines:
+ f.write(line)
+ finally:
+ f.close()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/websdk/jinja2/_markupsafe/_constants.py b/websdk/jinja2/_markupsafe/_constants.py
new file mode 100644
index 0000000..919bf03
--- /dev/null
+++ b/websdk/jinja2/_markupsafe/_constants.py
@@ -0,0 +1,267 @@
+# -*- coding: utf-8 -*-
+"""
+ markupsafe._constants
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Highlevel implementation of the Markup string.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+
+HTML_ENTITIES = {
+ 'AElig': 198,
+ 'Aacute': 193,
+ 'Acirc': 194,
+ 'Agrave': 192,
+ 'Alpha': 913,
+ 'Aring': 197,
+ 'Atilde': 195,
+ 'Auml': 196,
+ 'Beta': 914,
+ 'Ccedil': 199,
+ 'Chi': 935,
+ 'Dagger': 8225,
+ 'Delta': 916,
+ 'ETH': 208,
+ 'Eacute': 201,
+ 'Ecirc': 202,
+ 'Egrave': 200,
+ 'Epsilon': 917,
+ 'Eta': 919,
+ 'Euml': 203,
+ 'Gamma': 915,
+ 'Iacute': 205,
+ 'Icirc': 206,
+ 'Igrave': 204,
+ 'Iota': 921,
+ 'Iuml': 207,
+ 'Kappa': 922,
+ 'Lambda': 923,
+ 'Mu': 924,
+ 'Ntilde': 209,
+ 'Nu': 925,
+ 'OElig': 338,
+ 'Oacute': 211,
+ 'Ocirc': 212,
+ 'Ograve': 210,
+ 'Omega': 937,
+ 'Omicron': 927,
+ 'Oslash': 216,
+ 'Otilde': 213,
+ 'Ouml': 214,
+ 'Phi': 934,
+ 'Pi': 928,
+ 'Prime': 8243,
+ 'Psi': 936,
+ 'Rho': 929,
+ 'Scaron': 352,
+ 'Sigma': 931,
+ 'THORN': 222,
+ 'Tau': 932,
+ 'Theta': 920,
+ 'Uacute': 218,
+ 'Ucirc': 219,
+ 'Ugrave': 217,
+ 'Upsilon': 933,
+ 'Uuml': 220,
+ 'Xi': 926,
+ 'Yacute': 221,
+ 'Yuml': 376,
+ 'Zeta': 918,
+ 'aacute': 225,
+ 'acirc': 226,
+ 'acute': 180,
+ 'aelig': 230,
+ 'agrave': 224,
+ 'alefsym': 8501,
+ 'alpha': 945,
+ 'amp': 38,
+ 'and': 8743,
+ 'ang': 8736,
+ 'apos': 39,
+ 'aring': 229,
+ 'asymp': 8776,
+ 'atilde': 227,
+ 'auml': 228,
+ 'bdquo': 8222,
+ 'beta': 946,
+ 'brvbar': 166,
+ 'bull': 8226,
+ 'cap': 8745,
+ 'ccedil': 231,
+ 'cedil': 184,
+ 'cent': 162,
+ 'chi': 967,
+ 'circ': 710,
+ 'clubs': 9827,
+ 'cong': 8773,
+ 'copy': 169,
+ 'crarr': 8629,
+ 'cup': 8746,
+ 'curren': 164,
+ 'dArr': 8659,
+ 'dagger': 8224,
+ 'darr': 8595,
+ 'deg': 176,
+ 'delta': 948,
+ 'diams': 9830,
+ 'divide': 247,
+ 'eacute': 233,
+ 'ecirc': 234,
+ 'egrave': 232,
+ 'empty': 8709,
+ 'emsp': 8195,
+ 'ensp': 8194,
+ 'epsilon': 949,
+ 'equiv': 8801,
+ 'eta': 951,
+ 'eth': 240,
+ 'euml': 235,
+ 'euro': 8364,
+ 'exist': 8707,
+ 'fnof': 402,
+ 'forall': 8704,
+ 'frac12': 189,
+ 'frac14': 188,
+ 'frac34': 190,
+ 'frasl': 8260,
+ 'gamma': 947,
+ 'ge': 8805,
+ 'gt': 62,
+ 'hArr': 8660,
+ 'harr': 8596,
+ 'hearts': 9829,
+ 'hellip': 8230,
+ 'iacute': 237,
+ 'icirc': 238,
+ 'iexcl': 161,
+ 'igrave': 236,
+ 'image': 8465,
+ 'infin': 8734,
+ 'int': 8747,
+ 'iota': 953,
+ 'iquest': 191,
+ 'isin': 8712,
+ 'iuml': 239,
+ 'kappa': 954,
+ 'lArr': 8656,
+ 'lambda': 955,
+ 'lang': 9001,
+ 'laquo': 171,
+ 'larr': 8592,
+ 'lceil': 8968,
+ 'ldquo': 8220,
+ 'le': 8804,
+ 'lfloor': 8970,
+ 'lowast': 8727,
+ 'loz': 9674,
+ 'lrm': 8206,
+ 'lsaquo': 8249,
+ 'lsquo': 8216,
+ 'lt': 60,
+ 'macr': 175,
+ 'mdash': 8212,
+ 'micro': 181,
+ 'middot': 183,
+ 'minus': 8722,
+ 'mu': 956,
+ 'nabla': 8711,
+ 'nbsp': 160,
+ 'ndash': 8211,
+ 'ne': 8800,
+ 'ni': 8715,
+ 'not': 172,
+ 'notin': 8713,
+ 'nsub': 8836,
+ 'ntilde': 241,
+ 'nu': 957,
+ 'oacute': 243,
+ 'ocirc': 244,
+ 'oelig': 339,
+ 'ograve': 242,
+ 'oline': 8254,
+ 'omega': 969,
+ 'omicron': 959,
+ 'oplus': 8853,
+ 'or': 8744,
+ 'ordf': 170,
+ 'ordm': 186,
+ 'oslash': 248,
+ 'otilde': 245,
+ 'otimes': 8855,
+ 'ouml': 246,
+ 'para': 182,
+ 'part': 8706,
+ 'permil': 8240,
+ 'perp': 8869,
+ 'phi': 966,
+ 'pi': 960,
+ 'piv': 982,
+ 'plusmn': 177,
+ 'pound': 163,
+ 'prime': 8242,
+ 'prod': 8719,
+ 'prop': 8733,
+ 'psi': 968,
+ 'quot': 34,
+ 'rArr': 8658,
+ 'radic': 8730,
+ 'rang': 9002,
+ 'raquo': 187,
+ 'rarr': 8594,
+ 'rceil': 8969,
+ 'rdquo': 8221,
+ 'real': 8476,
+ 'reg': 174,
+ 'rfloor': 8971,
+ 'rho': 961,
+ 'rlm': 8207,
+ 'rsaquo': 8250,
+ 'rsquo': 8217,
+ 'sbquo': 8218,
+ 'scaron': 353,
+ 'sdot': 8901,
+ 'sect': 167,
+ 'shy': 173,
+ 'sigma': 963,
+ 'sigmaf': 962,
+ 'sim': 8764,
+ 'spades': 9824,
+ 'sub': 8834,
+ 'sube': 8838,
+ 'sum': 8721,
+ 'sup': 8835,
+ 'sup1': 185,
+ 'sup2': 178,
+ 'sup3': 179,
+ 'supe': 8839,
+ 'szlig': 223,
+ 'tau': 964,
+ 'there4': 8756,
+ 'theta': 952,
+ 'thetasym': 977,
+ 'thinsp': 8201,
+ 'thorn': 254,
+ 'tilde': 732,
+ 'times': 215,
+ 'trade': 8482,
+ 'uArr': 8657,
+ 'uacute': 250,
+ 'uarr': 8593,
+ 'ucirc': 251,
+ 'ugrave': 249,
+ 'uml': 168,
+ 'upsih': 978,
+ 'upsilon': 965,
+ 'uuml': 252,
+ 'weierp': 8472,
+ 'xi': 958,
+ 'yacute': 253,
+ 'yen': 165,
+ 'yuml': 255,
+ 'zeta': 950,
+ 'zwj': 8205,
+ 'zwnj': 8204
+}
diff --git a/websdk/jinja2/_markupsafe/_native.py b/websdk/jinja2/_markupsafe/_native.py
new file mode 100644
index 0000000..7b95828
--- /dev/null
+++ b/websdk/jinja2/_markupsafe/_native.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+"""
+ markupsafe._native
+ ~~~~~~~~~~~~~~~~~~
+
+ Native Python implementation the C module is not compiled.
+
+ :copyright: (c) 2010 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+from jinja2._markupsafe import Markup
+
+
+def escape(s):
+ """Convert the characters &, <, >, ' and " 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.
+ """
+ if hasattr(s, '__html__'):
+ return s.__html__()
+ return Markup(unicode(s)
+ .replace('&', '&amp;')
+ .replace('>', '&gt;')
+ .replace('<', '&lt;')
+ .replace("'", '&#39;')
+ .replace('"', '&#34;')
+ )
+
+
+def escape_silent(s):
+ """Like :func:`escape` but converts `None` into an empty
+ markup string.
+ """
+ if s is None:
+ return Markup()
+ return escape(s)
+
+
+def soft_unicode(s):
+ """Make a string unicode if it isn't already. That way a markup
+ string is not converted back to unicode.
+ """
+ if not isinstance(s, unicode):
+ s = unicode(s)
+ return s
diff --git a/websdk/jinja2/_markupsafe/tests.py b/websdk/jinja2/_markupsafe/tests.py
new file mode 100644
index 0000000..c1ce394
--- /dev/null
+++ b/websdk/jinja2/_markupsafe/tests.py
@@ -0,0 +1,80 @@
+import gc
+import unittest
+from jinja2._markupsafe import Markup, escape, escape_silent
+
+
+class MarkupTestCase(unittest.TestCase):
+
+ def test_markup_operations(self):
+ # adding two strings should escape the unsafe one
+ unsafe = '<script type="application/x-some-script">alert("foo");</script>'
+ safe = Markup('<em>username</em>')
+ assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe)
+
+ # string interpolations are safe to use too
+ assert Markup('<em>%s</em>') % '<bad user>' == \
+ '<em>&lt;bad user&gt;</em>'
+ assert Markup('<em>%(username)s</em>') % {
+ 'username': '<bad user>'
+ } == '<em>&lt;bad user&gt;</em>'
+
+ # an escaped object is markup too
+ assert type(Markup('foo') + 'bar') is Markup
+
+ # and it implements __html__ by returning itself
+ x = Markup("foo")
+ assert x.__html__() is x
+
+ # it also knows how to treat __html__ objects
+ class Foo(object):
+ def __html__(self):
+ return '<em>awesome</em>'
+ def __unicode__(self):
+ return 'awesome'
+ assert Markup(Foo()) == '<em>awesome</em>'
+ assert Markup('<strong>%s</strong>') % Foo() == \
+ '<strong><em>awesome</em></strong>'
+
+ # escaping and unescaping
+ assert escape('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
+ assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
+ assert Markup("&lt;test&gt;").unescape() == "<test>"
+
+ def test_all_set(self):
+ import jinja2._markupsafe as markup
+ for item in markup.__all__:
+ getattr(markup, item)
+
+ def test_escape_silent(self):
+ assert escape_silent(None) == Markup()
+ assert escape(None) == Markup(None)
+ assert escape_silent('<foo>') == Markup(u'&lt;foo&gt;')
+
+
+class MarkupLeakTestCase(unittest.TestCase):
+
+ def test_markup_leaks(self):
+ counts = set()
+ for count in xrange(20):
+ for item in xrange(1000):
+ escape("foo")
+ escape("<foo>")
+ escape(u"foo")
+ escape(u"<foo>")
+ counts.add(len(gc.get_objects()))
+ assert len(counts) == 1, 'ouch, c extension seems to leak objects'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(MarkupTestCase))
+
+ # this test only tests the c extension
+ if not hasattr(escape, 'func_code'):
+ suite.addTest(unittest.makeSuite(MarkupLeakTestCase))
+
+ return suite
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='suite')
diff --git a/websdk/jinja2/_stringdefs.py b/websdk/jinja2/_stringdefs.py
new file mode 100644
index 0000000..1161b7f
--- /dev/null
+++ b/websdk/jinja2/_stringdefs.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2._stringdefs
+ ~~~~~~~~~~~~~~~~~~
+
+ Strings of all Unicode characters of a certain category.
+ Used for matching in Unicode-aware languages. Run to regenerate.
+
+ Inspired by chartypes_create.py from the MoinMoin project, original
+ implementation from Pygments.
+
+ :copyright: Copyright 2006-2009 by the Jinja team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+Cc = u'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f'
+
+Cf = u'\xad\u0600\u0601\u0602\u0603\u06dd\u070f\u17b4\u17b5\u200b\u200c\u200d\u200e\u200f\u202a\u202b\u202c\u202d\u202e\u2060\u2061\u2062\u2063\u206a\u206b\u206c\u206d\u206e\u206f\ufeff\ufff9\ufffa\ufffb'
+
+Cn = u'\u0242\u0243\u0244\u0245\u0246\u0247\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u0370\u0371\u0372\u0373\u0376\u0377\u0378\u0379\u037b\u037c\u037d\u037f\u0380\u0381\u0382\u0383\u038b\u038d\u03a2\u03cf\u0487\u04cf\u04fa\u04fb\u04fc\u04fd\u04fe\u04ff\u0510\u0511\u0512\u0513\u0514\u0515\u0516\u0517\u0518\u0519\u051a\u051b\u051c\u051d\u051e\u051f\u0520\u0521\u0522\u0523\u0524\u0525\u0526\u0527\u0528\u0529\u052a\u052b\u052c\u052d\u052e\u052f\u0530\u0557\u0558\u0560\u0588\u058b\u058c\u058d\u058e\u058f\u0590\u05ba\u05c8\u05c9\u05ca\u05cb\u05cc\u05cd\u05ce\u05cf\u05eb\u05ec\u05ed\u05ee\u05ef\u05f5\u05f6\u05f7\u05f8\u05f9\u05fa\u05fb\u05fc\u05fd\u05fe\u05ff\u0604\u0605\u0606\u0607\u0608\u0609\u060a\u0616\u0617\u0618\u0619\u061a\u061c\u061d\u0620\u063b\u063c\u063d\u063e\u063f\u065f\u070e\u074b\u074c\u076e\u076f\u0770\u0771\u0772\u0773\u0774\u0775\u0776\u0777\u0778\u0779\u077a\u077b\u077c\u077d\u077e\u077f\u07b2\u07b3\u07b4\u07b5\u07b6\u07b7\u07b8\u07b9\u07ba\u07bb\u07bc\u07bd\u07be\u07bf\u07c0\u07c1\u07c2\u07c3\u07c4\u07c5\u07c6\u07c7\u07c8\u07c9\u07ca\u07cb\u07cc\u07cd\u07ce\u07cf\u07d0\u07d1\u07d2\u07d3\u07d4\u07d5\u07d6\u07d7\u07d8\u07d9\u07da\u07db\u07dc\u07dd\u07de\u07df\u07e0\u07e1\u07e2\u07e3\u07e4\u07e5\u07e6\u07e7\u07e8\u07e9\u07ea\u07eb\u07ec\u07ed\u07ee\u07ef\u07f0\u07f1\u07f2\u07f3\u07f4\u07f5\u07f6\u07f7\u07f8\u07f9\u07fa\u07fb\u07fc\u07fd\u07fe\u07ff\u0800\u0801\u0802\u0803\u0804\u0805\u0806\u0807\u0808\u0809\u080a\u080b\u080c\u080d\u080e\u080f\u0810\u0811\u0812\u0813\u0814\u0815\u0816\u0817\u0818\u0819\u081a\u081b\u081c\u081d\u081e\u081f\u0820\u0821\u0822\u0823\u0824\u0825\u0826\u0827\u0828\u0829\u082a\u082b\u082c\u082d\u082e\u082f\u0830\u0831\u0832\u0833\u0834\u0835\u0836\u0837\u0838\u0839\u083a\u083b\u083c\u083d\u083e\u083f\u0840\u0841\u0842\u0843\u0844\u0845\u0846\u0847\u0848\u0849\u084a\u084b\u084c\u084d\u084e\u084f\u0850\u0851\u0852\u0853\u0854\u0855\u0856\u0857\u0858\u0859\u085a\u085b\u085c\u085d\u085e\u085f\u0860\u0861\u0862\u0863\u0864\u0865\u0866\u0867\u0868\u0869\u086a\u086b\u086c\u086d\u086e\u086f\u0870\u0871\u0872\u0873\u0874\u0875\u0876\u0877\u0878\u0879\u087a\u087b\u087c\u087d\u087e\u087f\u0880\u0881\u0882\u0883\u0884\u0885\u0886\u0887\u0888\u0889\u088a\u088b\u088c\u088d\u088e\u088f\u0890\u0891\u0892\u0893\u0894\u0895\u0896\u0897\u0898\u0899\u089a\u089b\u089c\u089d\u089e\u089f\u08a0\u08a1\u08a2\u08a3\u08a4\u08a5\u08a6\u08a7\u08a8\u08a9\u08aa\u08ab\u08ac\u08ad\u08ae\u08af\u08b0\u08b1\u08b2\u08b3\u08b4\u08b5\u08b6\u08b7\u08b8\u08b9\u08ba\u08bb\u08bc\u08bd\u08be\u08bf\u08c0\u08c1\u08c2\u08c3\u08c4\u08c5\u08c6\u08c7\u08c8\u08c9\u08ca\u08cb\u08cc\u08cd\u08ce\u08cf\u08d0\u08d1\u08d2\u08d3\u08d4\u08d5\u08d6\u08d7\u08d8\u08d9\u08da\u08db\u08dc\u08dd\u08de\u08df\u08e0\u08e1\u08e2\u08e3\u08e4\u08e5\u08e6\u08e7\u08e8\u08e9\u08ea\u08eb\u08ec\u08ed\u08ee\u08ef\u08f0\u08f1\u08f2\u08f3\u08f4\u08f5\u08f6\u08f7\u08f8\u08f9\u08fa\u08fb\u08fc\u08fd\u08fe\u08ff\u0900\u093a\u093b\u094e\u094f\u0955\u0956\u0957\u0971\u0972\u0973\u0974\u0975\u0976\u0977\u0978\u0979\u097a\u097b\u097c\u097e\u097f\u0980\u0984\u098d\u098e\u0991\u0992\u09a9\u09b1\u09b3\u09b4\u09b5\u09ba\u09bb\u09c5\u09c6\u09c9\u09ca\u09cf\u09d0\u09d1\u09d2\u09d3\u09d4\u09d5\u09d6\u09d8\u09d9\u09da\u09db\u09de\u09e4\u09e5\u09fb\u09fc\u09fd\u09fe\u09ff\u0a00\u0a04\u0a0b\u0a0c\u0a0d\u0a0e\u0a11\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a\u0a3b\u0a3d\u0a43\u0a44\u0a45\u0a46\u0a49\u0a4a\u0a4e\u0a4f\u0a50\u0a51\u0a52\u0a53\u0a54\u0a55\u0a56\u0a57\u0a58\u0a5d\u0a5f\u0a60\u0a61\u0a62\u0a63\u0a64\u0a65\u0a75\u0a76\u0a77\u0a78\u0a79\u0a7a\u0a7b\u0a7c\u0a7d\u0a7e\u0a7f\u0a80\u0a84\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba\u0abb\u0ac6\u0aca\u0ace\u0acf\u0ad1\u0ad2\u0ad3\u0ad4\u0ad5\u0ad6\u0ad7\u0ad8\u0ad9\u0ada\u0adb\u0adc\u0add\u0ade\u0adf\u0ae4\u0ae5\u0af0\u0af2\u0af3\u0af4\u0af5\u0af6\u0af7\u0af8\u0af9\u0afa\u0afb\u0afc\u0afd\u0afe\u0aff\u0b00\u0b04\u0b0d\u0b0e\u0b11\u0b12\u0b29\u0b31\u0b34\u0b3a\u0b3b\u0b44\u0b45\u0b46\u0b49\u0b4a\u0b4e\u0b4f\u0b50\u0b51\u0b52\u0b53\u0b54\u0b55\u0b58\u0b59\u0b5a\u0b5b\u0b5e\u0b62\u0b63\u0b64\u0b65\u0b72\u0b73\u0b74\u0b75\u0b76\u0b77\u0b78\u0b79\u0b7a\u0b7b\u0b7c\u0b7d\u0b7e\u0b7f\u0b80\u0b81\u0b84\u0b8b\u0b8c\u0b8d\u0b91\u0b96\u0b97\u0b98\u0b9b\u0b9d\u0ba0\u0ba1\u0ba2\u0ba5\u0ba6\u0ba7\u0bab\u0bac\u0bad\u0bba\u0bbb\u0bbc\u0bbd\u0bc3\u0bc4\u0bc5\u0bc9\u0bce\u0bcf\u0bd0\u0bd1\u0bd2\u0bd3\u0bd4\u0bd5\u0bd6\u0bd8\u0bd9\u0bda\u0bdb\u0bdc\u0bdd\u0bde\u0bdf\u0be0\u0be1\u0be2\u0be3\u0be4\u0be5\u0bfb\u0bfc\u0bfd\u0bfe\u0bff\u0c00\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a\u0c3b\u0c3c\u0c3d\u0c45\u0c49\u0c4e\u0c4f\u0c50\u0c51\u0c52\u0c53\u0c54\u0c57\u0c58\u0c59\u0c5a\u0c5b\u0c5c\u0c5d\u0c5e\u0c5f\u0c62\u0c63\u0c64\u0c65\u0c70\u0c71\u0c72\u0c73\u0c74\u0c75\u0c76\u0c77\u0c78\u0c79\u0c7a\u0c7b\u0c7c\u0c7d\u0c7e\u0c7f\u0c80\u0c81\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba\u0cbb\u0cc5\u0cc9\u0cce\u0ccf\u0cd0\u0cd1\u0cd2\u0cd3\u0cd4\u0cd7\u0cd8\u0cd9\u0cda\u0cdb\u0cdc\u0cdd\u0cdf\u0ce2\u0ce3\u0ce4\u0ce5\u0cf0\u0cf1\u0cf2\u0cf3\u0cf4\u0cf5\u0cf6\u0cf7\u0cf8\u0cf9\u0cfa\u0cfb\u0cfc\u0cfd\u0cfe\u0cff\u0d00\u0d01\u0d04\u0d0d\u0d11\u0d29\u0d3a\u0d3b\u0d3c\u0d3d\u0d44\u0d45\u0d49\u0d4e\u0d4f\u0d50\u0d51\u0d52\u0d53\u0d54\u0d55\u0d56\u0d58\u0d59\u0d5a\u0d5b\u0d5c\u0d5d\u0d5e\u0d5f\u0d62\u0d63\u0d64\u0d65\u0d70\u0d71\u0d72\u0d73\u0d74\u0d75\u0d76\u0d77\u0d78\u0d79\u0d7a\u0d7b\u0d7c\u0d7d\u0d7e\u0d7f\u0d80\u0d81\u0d84\u0d97\u0d98\u0d99\u0db2\u0dbc\u0dbe\u0dbf\u0dc7\u0dc8\u0dc9\u0dcb\u0dcc\u0dcd\u0dce\u0dd5\u0dd7\u0de0\u0de1\u0de2\u0de3\u0de4\u0de5\u0de6\u0de7\u0de8\u0de9\u0dea\u0deb\u0dec\u0ded\u0dee\u0def\u0df0\u0df1\u0df5\u0df6\u0df7\u0df8\u0df9\u0dfa\u0dfb\u0dfc\u0dfd\u0dfe\u0dff\u0e00\u0e3b\u0e3c\u0e3d\u0e3e\u0e5c\u0e5d\u0e5e\u0e5f\u0e60\u0e61\u0e62\u0e63\u0e64\u0e65\u0e66\u0e67\u0e68\u0e69\u0e6a\u0e6b\u0e6c\u0e6d\u0e6e\u0e6f\u0e70\u0e71\u0e72\u0e73\u0e74\u0e75\u0e76\u0e77\u0e78\u0e79\u0e7a\u0e7b\u0e7c\u0e7d\u0e7e\u0e7f\u0e80\u0e83\u0e85\u0e86\u0e89\u0e8b\u0e8c\u0e8e\u0e8f\u0e90\u0e91\u0e92\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8\u0ea9\u0eac\u0eba\u0ebe\u0ebf\u0ec5\u0ec7\u0ece\u0ecf\u0eda\u0edb\u0ede\u0edf\u0ee0\u0ee1\u0ee2\u0ee3\u0ee4\u0ee5\u0ee6\u0ee7\u0ee8\u0ee9\u0eea\u0eeb\u0eec\u0eed\u0eee\u0eef\u0ef0\u0ef1\u0ef2\u0ef3\u0ef4\u0ef5\u0ef6\u0ef7\u0ef8\u0ef9\u0efa\u0efb\u0efc\u0efd\u0efe\u0eff\u0f48\u0f6b\u0f6c\u0f6d\u0f6e\u0f6f\u0f70\u0f8c\u0f8d\u0f8e\u0f8f\u0f98\u0fbd\u0fcd\u0fce\u0fd2\u0fd3\u0fd4\u0fd5\u0fd6\u0fd7\u0fd8\u0fd9\u0fda\u0fdb\u0fdc\u0fdd\u0fde\u0fdf\u0fe0\u0fe1\u0fe2\u0fe3\u0fe4\u0fe5\u0fe6\u0fe7\u0fe8\u0fe9\u0fea\u0feb\u0fec\u0fed\u0fee\u0fef\u0ff0\u0ff1\u0ff2\u0ff3\u0ff4\u0ff5\u0ff6\u0ff7\u0ff8\u0ff9\u0ffa\u0ffb\u0ffc\u0ffd\u0ffe\u0fff\u1022\u1028\u102b\u1033\u1034\u1035\u103a\u103b\u103c\u103d\u103e\u103f\u105a\u105b\u105c\u105d\u105e\u105f\u1060\u1061\u1062\u1063\u1064\u1065\u1066\u1067\u1068\u1069\u106a\u106b\u106c\u106d\u106e\u106f\u1070\u1071\u1072\u1073\u1074\u1075\u1076\u1077\u1078\u1079\u107a\u107b\u107c\u107d\u107e\u107f\u1080\u1081\u1082\u1083\u1084\u1085\u1086\u1087\u1088\u1089\u108a\u108b\u108c\u108d\u108e\u108f\u1090\u1091\u1092\u1093\u1094\u1095\u1096\u1097\u1098\u1099\u109a\u109b\u109c\u109d\u109e\u109f\u10c6\u10c7\u10c8\u10c9\u10ca\u10cb\u10cc\u10cd\u10ce\u10cf\u10fd\u10fe\u10ff\u115a\u115b\u115c\u115d\u115e\u11a3\u11a4\u11a5\u11a6\u11a7\u11fa\u11fb\u11fc\u11fd\u11fe\u11ff\u1249\u124e\u124f\u1257\u1259\u125e\u125f\u1289\u128e\u128f\u12b1\u12b6\u12b7\u12bf\u12c1\u12c6\u12c7\u12d7\u1311\u1316\u1317\u135b\u135c\u135d\u135e\u137d\u137e\u137f\u139a\u139b\u139c\u139d\u139e\u139f\u13f5\u13f6\u13f7\u13f8\u13f9\u13fa\u13fb\u13fc\u13fd\u13fe\u13ff\u1400\u1677\u1678\u1679\u167a\u167b\u167c\u167d\u167e\u167f\u169d\u169e\u169f\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u16f9\u16fa\u16fb\u16fc\u16fd\u16fe\u16ff\u170d\u1715\u1716\u1717\u1718\u1719\u171a\u171b\u171c\u171d\u171e\u171f\u1737\u1738\u1739\u173a\u173b\u173c\u173d\u173e\u173f\u1754\u1755\u1756\u1757\u1758\u1759\u175a\u175b\u175c\u175d\u175e\u175f\u176d\u1771\u1774\u1775\u1776\u1777\u1778\u1779\u177a\u177b\u177c\u177d\u177e\u177f\u17de\u17df\u17ea\u17eb\u17ec\u17ed\u17ee\u17ef\u17fa\u17fb\u17fc\u17fd\u17fe\u17ff\u180f\u181a\u181b\u181c\u181d\u181e\u181f\u1878\u1879\u187a\u187b\u187c\u187d\u187e\u187f\u18aa\u18ab\u18ac\u18ad\u18ae\u18af\u18b0\u18b1\u18b2\u18b3\u18b4\u18b5\u18b6\u18b7\u18b8\u18b9\u18ba\u18bb\u18bc\u18bd\u18be\u18bf\u18c0\u18c1\u18c2\u18c3\u18c4\u18c5\u18c6\u18c7\u18c8\u18c9\u18ca\u18cb\u18cc\u18cd\u18ce\u18cf\u18d0\u18d1\u18d2\u18d3\u18d4\u18d5\u18d6\u18d7\u18d8\u18d9\u18da\u18db\u18dc\u18dd\u18de\u18df\u18e0\u18e1\u18e2\u18e3\u18e4\u18e5\u18e6\u18e7\u18e8\u18e9\u18ea\u18eb\u18ec\u18ed\u18ee\u18ef\u18f0\u18f1\u18f2\u18f3\u18f4\u18f5\u18f6\u18f7\u18f8\u18f9\u18fa\u18fb\u18fc\u18fd\u18fe\u18ff\u191d\u191e\u191f\u192c\u192d\u192e\u192f\u193c\u193d\u193e\u193f\u1941\u1942\u1943\u196e\u196f\u1975\u1976\u1977\u1978\u1979\u197a\u197b\u197c\u197d\u197e\u197f\u19aa\u19ab\u19ac\u19ad\u19ae\u19af\u19ca\u19cb\u19cc\u19cd\u19ce\u19cf\u19da\u19db\u19dc\u19dd\u1a1c\u1a1d\u1a20\u1a21\u1a22\u1a23\u1a24\u1a25\u1a26\u1a27\u1a28\u1a29\u1a2a\u1a2b\u1a2c\u1a2d\u1a2e\u1a2f\u1a30\u1a31\u1a32\u1a33\u1a34\u1a35\u1a36\u1a37\u1a38\u1a39\u1a3a\u1a3b\u1a3c\u1a3d\u1a3e\u1a3f\u1a40\u1a41\u1a42\u1a43\u1a44\u1a45\u1a46\u1a47\u1a48\u1a49\u1a4a\u1a4b\u1a4c\u1a4d\u1a4e\u1a4f\u1a50\u1a51\u1a52\u1a53\u1a54\u1a55\u1a56\u1a57\u1a58\u1a59\u1a5a\u1a5b\u1a5c\u1a5d\u1a5e\u1a5f\u1a60\u1a61\u1a62\u1a63\u1a64\u1a65\u1a66\u1a67\u1a68\u1a69\u1a6a\u1a6b\u1a6c\u1a6d\u1a6e\u1a6f\u1a70\u1a71\u1a72\u1a73\u1a74\u1a75\u1a76\u1a77\u1a78\u1a79\u1a7a\u1a7b\u1a7c\u1a7d\u1a7e\u1a7f\u1a80\u1a81\u1a82\u1a83\u1a84\u1a85\u1a86\u1a87\u1a88\u1a89\u1a8a\u1a8b\u1a8c\u1a8d\u1a8e\u1a8f\u1a90\u1a91\u1a92\u1a93\u1a94\u1a95\u1a96\u1a97\u1a98\u1a99\u1a9a\u1a9b\u1a9c\u1a9d\u1a9e\u1a9f\u1aa0\u1aa1\u1aa2\u1aa3\u1aa4\u1aa5\u1aa6\u1aa7\u1aa8\u1aa9\u1aaa\u1aab\u1aac\u1aad\u1aae\u1aaf\u1ab0\u1ab1\u1ab2\u1ab3\u1ab4\u1ab5\u1ab6\u1ab7\u1ab8\u1ab9\u1aba\u1abb\u1abc\u1abd\u1abe\u1abf\u1ac0\u1ac1\u1ac2\u1ac3\u1ac4\u1ac5\u1ac6\u1ac7\u1ac8\u1ac9\u1aca\u1acb\u1acc\u1acd\u1ace\u1acf\u1ad0\u1ad1\u1ad2\u1ad3\u1ad4\u1ad5\u1ad6\u1ad7\u1ad8\u1ad9\u1ada\u1adb\u1adc\u1add\u1ade\u1adf\u1ae0\u1ae1\u1ae2\u1ae3\u1ae4\u1ae5\u1ae6\u1ae7\u1ae8\u1ae9\u1aea\u1aeb\u1aec\u1aed\u1aee\u1aef\u1af0\u1af1\u1af2\u1af3\u1af4\u1af5\u1af6\u1af7\u1af8\u1af9\u1afa\u1afb\u1afc\u1afd\u1afe\u1aff\u1b00\u1b01\u1b02\u1b03\u1b04\u1b05\u1b06\u1b07\u1b08\u1b09\u1b0a\u1b0b\u1b0c\u1b0d\u1b0e\u1b0f\u1b10\u1b11\u1b12\u1b13\u1b14\u1b15\u1b16\u1b17\u1b18\u1b19\u1b1a\u1b1b\u1b1c\u1b1d\u1b1e\u1b1f\u1b20\u1b21\u1b22\u1b23\u1b24\u1b25\u1b26\u1b27\u1b28\u1b29\u1b2a\u1b2b\u1b2c\u1b2d\u1b2e\u1b2f\u1b30\u1b31\u1b32\u1b33\u1b34\u1b35\u1b36\u1b37\u1b38\u1b39\u1b3a\u1b3b\u1b3c\u1b3d\u1b3e\u1b3f\u1b40\u1b41\u1b42\u1b43\u1b44\u1b45\u1b46\u1b47\u1b48\u1b49\u1b4a\u1b4b\u1b4c\u1b4d\u1b4e\u1b4f\u1b50\u1b51\u1b52\u1b53\u1b54\u1b55\u1b56\u1b57\u1b58\u1b59\u1b5a\u1b5b\u1b5c\u1b5d\u1b5e\u1b5f\u1b60\u1b61\u1b62\u1b63\u1b64\u1b65\u1b66\u1b67\u1b68\u1b69\u1b6a\u1b6b\u1b6c\u1b6d\u1b6e\u1b6f\u1b70\u1b71\u1b72\u1b73\u1b74\u1b75\u1b76\u1b77\u1b78\u1b79\u1b7a\u1b7b\u1b7c\u1b7d\u1b7e\u1b7f\u1b80\u1b81\u1b82\u1b83\u1b84\u1b85\u1b86\u1b87\u1b88\u1b89\u1b8a\u1b8b\u1b8c\u1b8d\u1b8e\u1b8f\u1b90\u1b91\u1b92\u1b93\u1b94\u1b95\u1b96\u1b97\u1b98\u1b99\u1b9a\u1b9b\u1b9c\u1b9d\u1b9e\u1b9f\u1ba0\u1ba1\u1ba2\u1ba3\u1ba4\u1ba5\u1ba6\u1ba7\u1ba8\u1ba9\u1baa\u1bab\u1bac\u1bad\u1bae\u1baf\u1bb0\u1bb1\u1bb2\u1bb3\u1bb4\u1bb5\u1bb6\u1bb7\u1bb8\u1bb9\u1bba\u1bbb\u1bbc\u1bbd\u1bbe\u1bbf\u1bc0\u1bc1\u1bc2\u1bc3\u1bc4\u1bc5\u1bc6\u1bc7\u1bc8\u1bc9\u1bca\u1bcb\u1bcc\u1bcd\u1bce\u1bcf\u1bd0\u1bd1\u1bd2\u1bd3\u1bd4\u1bd5\u1bd6\u1bd7\u1bd8\u1bd9\u1bda\u1bdb\u1bdc\u1bdd\u1bde\u1bdf\u1be0\u1be1\u1be2\u1be3\u1be4\u1be5\u1be6\u1be7\u1be8\u1be9\u1bea\u1beb\u1bec\u1bed\u1bee\u1bef\u1bf0\u1bf1\u1bf2\u1bf3\u1bf4\u1bf5\u1bf6\u1bf7\u1bf8\u1bf9\u1bfa\u1bfb\u1bfc\u1bfd\u1bfe\u1bff\u1c00\u1c01\u1c02\u1c03\u1c04\u1c05\u1c06\u1c07\u1c08\u1c09\u1c0a\u1c0b\u1c0c\u1c0d\u1c0e\u1c0f\u1c10\u1c11\u1c12\u1c13\u1c14\u1c15\u1c16\u1c17\u1c18\u1c19\u1c1a\u1c1b\u1c1c\u1c1d\u1c1e\u1c1f\u1c20\u1c21\u1c22\u1c23\u1c24\u1c25\u1c26\u1c27\u1c28\u1c29\u1c2a\u1c2b\u1c2c\u1c2d\u1c2e\u1c2f\u1c30\u1c31\u1c32\u1c33\u1c34\u1c35\u1c36\u1c37\u1c38\u1c39\u1c3a\u1c3b\u1c3c\u1c3d\u1c3e\u1c3f\u1c40\u1c41\u1c42\u1c43\u1c44\u1c45\u1c46\u1c47\u1c48\u1c49\u1c4a\u1c4b\u1c4c\u1c4d\u1c4e\u1c4f\u1c50\u1c51\u1c52\u1c53\u1c54\u1c55\u1c56\u1c57\u1c58\u1c59\u1c5a\u1c5b\u1c5c\u1c5d\u1c5e\u1c5f\u1c60\u1c61\u1c62\u1c63\u1c64\u1c65\u1c66\u1c67\u1c68\u1c69\u1c6a\u1c6b\u1c6c\u1c6d\u1c6e\u1c6f\u1c70\u1c71\u1c72\u1c73\u1c74\u1c75\u1c76\u1c77\u1c78\u1c79\u1c7a\u1c7b\u1c7c\u1c7d\u1c7e\u1c7f\u1c80\u1c81\u1c82\u1c83\u1c84\u1c85\u1c86\u1c87\u1c88\u1c89\u1c8a\u1c8b\u1c8c\u1c8d\u1c8e\u1c8f\u1c90\u1c91\u1c92\u1c93\u1c94\u1c95\u1c96\u1c97\u1c98\u1c99\u1c9a\u1c9b\u1c9c\u1c9d\u1c9e\u1c9f\u1ca0\u1ca1\u1ca2\u1ca3\u1ca4\u1ca5\u1ca6\u1ca7\u1ca8\u1ca9\u1caa\u1cab\u1cac\u1cad\u1cae\u1caf\u1cb0\u1cb1\u1cb2\u1cb3\u1cb4\u1cb5\u1cb6\u1cb7\u1cb8\u1cb9\u1cba\u1cbb\u1cbc\u1cbd\u1cbe\u1cbf\u1cc0\u1cc1\u1cc2\u1cc3\u1cc4\u1cc5\u1cc6\u1cc7\u1cc8\u1cc9\u1cca\u1ccb\u1ccc\u1ccd\u1cce\u1ccf\u1cd0\u1cd1\u1cd2\u1cd3\u1cd4\u1cd5\u1cd6\u1cd7\u1cd8\u1cd9\u1cda\u1cdb\u1cdc\u1cdd\u1cde\u1cdf\u1ce0\u1ce1\u1ce2\u1ce3\u1ce4\u1ce5\u1ce6\u1ce7\u1ce8\u1ce9\u1cea\u1ceb\u1cec\u1ced\u1cee\u1cef\u1cf0\u1cf1\u1cf2\u1cf3\u1cf4\u1cf5\u1cf6\u1cf7\u1cf8\u1cf9\u1cfa\u1cfb\u1cfc\u1cfd\u1cfe\u1cff\u1dc4\u1dc5\u1dc6\u1dc7\u1dc8\u1dc9\u1dca\u1dcb\u1dcc\u1dcd\u1dce\u1dcf\u1dd0\u1dd1\u1dd2\u1dd3\u1dd4\u1dd5\u1dd6\u1dd7\u1dd8\u1dd9\u1dda\u1ddb\u1ddc\u1ddd\u1dde\u1ddf\u1de0\u1de1\u1de2\u1de3\u1de4\u1de5\u1de6\u1de7\u1de8\u1de9\u1dea\u1deb\u1dec\u1ded\u1dee\u1def\u1df0\u1df1\u1df2\u1df3\u1df4\u1df5\u1df6\u1df7\u1df8\u1df9\u1dfa\u1dfb\u1dfc\u1dfd\u1dfe\u1dff\u1e9c\u1e9d\u1e9e\u1e9f\u1efa\u1efb\u1efc\u1efd\u1efe\u1eff\u1f16\u1f17\u1f1e\u1f1f\u1f46\u1f47\u1f4e\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e\u1f7f\u1fb5\u1fc5\u1fd4\u1fd5\u1fdc\u1ff0\u1ff1\u1ff5\u1fff\u2064\u2065\u2066\u2067\u2068\u2069\u2072\u2073\u208f\u2095\u2096\u2097\u2098\u2099\u209a\u209b\u209c\u209d\u209e\u209f\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb\u20bc\u20bd\u20be\u20bf\u20c0\u20c1\u20c2\u20c3\u20c4\u20c5\u20c6\u20c7\u20c8\u20c9\u20ca\u20cb\u20cc\u20cd\u20ce\u20cf\u20ec\u20ed\u20ee\u20ef\u20f0\u20f1\u20f2\u20f3\u20f4\u20f5\u20f6\u20f7\u20f8\u20f9\u20fa\u20fb\u20fc\u20fd\u20fe\u20ff\u214d\u214e\u214f\u2150\u2151\u2152\u2184\u2185\u2186\u2187\u2188\u2189\u218a\u218b\u218c\u218d\u218e\u218f\u23dc\u23dd\u23de\u23df\u23e0\u23e1\u23e2\u23e3\u23e4\u23e5\u23e6\u23e7\u23e8\u23e9\u23ea\u23eb\u23ec\u23ed\u23ee\u23ef\u23f0\u23f1\u23f2\u23f3\u23f4\u23f5\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u23fe\u23ff\u2427\u2428\u2429\u242a\u242b\u242c\u242d\u242e\u242f\u2430\u2431\u2432\u2433\u2434\u2435\u2436\u2437\u2438\u2439\u243a\u243b\u243c\u243d\u243e\u243f\u244b\u244c\u244d\u244e\u244f\u2450\u2451\u2452\u2453\u2454\u2455\u2456\u2457\u2458\u2459\u245a\u245b\u245c\u245d\u245e\u245f\u269d\u269e\u269f\u26b2\u26b3\u26b4\u26b5\u26b6\u26b7\u26b8\u26b9\u26ba\u26bb\u26bc\u26bd\u26be\u26bf\u26c0\u26c1\u26c2\u26c3\u26c4\u26c5\u26c6\u26c7\u26c8\u26c9\u26ca\u26cb\u26cc\u26cd\u26ce\u26cf\u26d0\u26d1\u26d2\u26d3\u26d4\u26d5\u26d6\u26d7\u26d8\u26d9\u26da\u26db\u26dc\u26dd\u26de\u26df\u26e0\u26e1\u26e2\u26e3\u26e4\u26e5\u26e6\u26e7\u26e8\u26e9\u26ea\u26eb\u26ec\u26ed\u26ee\u26ef\u26f0\u26f1\u26f2\u26f3\u26f4\u26f5\u26f6\u26f7\u26f8\u26f9\u26fa\u26fb\u26fc\u26fd\u26fe\u26ff\u2700\u2705\u270a\u270b\u2728\u274c\u274e\u2753\u2754\u2755\u2757\u275f\u2760\u2795\u2796\u2797\u27b0\u27bf\u27c7\u27c8\u27c9\u27ca\u27cb\u27cc\u27cd\u27ce\u27cf\u27ec\u27ed\u27ee\u27ef\u2b14\u2b15\u2b16\u2b17\u2b18\u2b19\u2b1a\u2b1b\u2b1c\u2b1d\u2b1e\u2b1f\u2b20\u2b21\u2b22\u2b23\u2b24\u2b25\u2b26\u2b27\u2b28\u2b29\u2b2a\u2b2b\u2b2c\u2b2d\u2b2e\u2b2f\u2b30\u2b31\u2b32\u2b33\u2b34\u2b35\u2b36\u2b37\u2b38\u2b39\u2b3a\u2b3b\u2b3c\u2b3d\u2b3e\u2b3f\u2b40\u2b41\u2b42\u2b43\u2b44\u2b45\u2b46\u2b47\u2b48\u2b49\u2b4a\u2b4b\u2b4c\u2b4d\u2b4e\u2b4f\u2b50\u2b51\u2b52\u2b53\u2b54\u2b55\u2b56\u2b57\u2b58\u2b59\u2b5a\u2b5b\u2b5c\u2b5d\u2b5e\u2b5f\u2b60\u2b61\u2b62\u2b63\u2b64\u2b65\u2b66\u2b67\u2b68\u2b69\u2b6a\u2b6b\u2b6c\u2b6d\u2b6e\u2b6f\u2b70\u2b71\u2b72\u2b73\u2b74\u2b75\u2b76\u2b77\u2b78\u2b79\u2b7a\u2b7b\u2b7c\u2b7d\u2b7e\u2b7f\u2b80\u2b81\u2b82\u2b83\u2b84\u2b85\u2b86\u2b87\u2b88\u2b89\u2b8a\u2b8b\u2b8c\u2b8d\u2b8e\u2b8f\u2b90\u2b91\u2b92\u2b93\u2b94\u2b95\u2b96\u2b97\u2b98\u2b99\u2b9a\u2b9b\u2b9c\u2b9d\u2b9e\u2b9f\u2ba0\u2ba1\u2ba2\u2ba3\u2ba4\u2ba5\u2ba6\u2ba7\u2ba8\u2ba9\u2baa\u2bab\u2bac\u2bad\u2bae\u2baf\u2bb0\u2bb1\u2bb2\u2bb3\u2bb4\u2bb5\u2bb6\u2bb7\u2bb8\u2bb9\u2bba\u2bbb\u2bbc\u2bbd\u2bbe\u2bbf\u2bc0\u2bc1\u2bc2\u2bc3\u2bc4\u2bc5\u2bc6\u2bc7\u2bc8\u2bc9\u2bca\u2bcb\u2bcc\u2bcd\u2bce\u2bcf\u2bd0\u2bd1\u2bd2\u2bd3\u2bd4\u2bd5\u2bd6\u2bd7\u2bd8\u2bd9\u2bda\u2bdb\u2bdc\u2bdd\u2bde\u2bdf\u2be0\u2be1\u2be2\u2be3\u2be4\u2be5\u2be6\u2be7\u2be8\u2be9\u2bea\u2beb\u2bec\u2bed\u2bee\u2bef\u2bf0\u2bf1\u2bf2\u2bf3\u2bf4\u2bf5\u2bf6\u2bf7\u2bf8\u2bf9\u2bfa\u2bfb\u2bfc\u2bfd\u2bfe\u2bff\u2c2f\u2c5f\u2c60\u2c61\u2c62\u2c63\u2c64\u2c65\u2c66\u2c67\u2c68\u2c69\u2c6a\u2c6b\u2c6c\u2c6d\u2c6e\u2c6f\u2c70\u2c71\u2c72\u2c73\u2c74\u2c75\u2c76\u2c77\u2c78\u2c79\u2c7a\u2c7b\u2c7c\u2c7d\u2c7e\u2c7f\u2ceb\u2cec\u2ced\u2cee\u2cef\u2cf0\u2cf1\u2cf2\u2cf3\u2cf4\u2cf5\u2cf6\u2cf7\u2cf8\u2d26\u2d27\u2d28\u2d29\u2d2a\u2d2b\u2d2c\u2d2d\u2d2e\u2d2f\u2d66\u2d67\u2d68\u2d69\u2d6a\u2d6b\u2d6c\u2d6d\u2d6e\u2d70\u2d71\u2d72\u2d73\u2d74\u2d75\u2d76\u2d77\u2d78\u2d79\u2d7a\u2d7b\u2d7c\u2d7d\u2d7e\u2d7f\u2d97\u2d98\u2d99\u2d9a\u2d9b\u2d9c\u2d9d\u2d9e\u2d9f\u2da7\u2daf\u2db7\u2dbf\u2dc7\u2dcf\u2dd7\u2ddf\u2de0\u2de1\u2de2\u2de3\u2de4\u2de5\u2de6\u2de7\u2de8\u2de9\u2dea\u2deb\u2dec\u2ded\u2dee\u2def\u2df0\u2df1\u2df2\u2df3\u2df4\u2df5\u2df6\u2df7\u2df8\u2df9\u2dfa\u2dfb\u2dfc\u2dfd\u2dfe\u2dff\u2e18\u2e19\u2e1a\u2e1b\u2e1e\u2e1f\u2e20\u2e21\u2e22\u2e23\u2e24\u2e25\u2e26\u2e27\u2e28\u2e29\u2e2a\u2e2b\u2e2c\u2e2d\u2e2e\u2e2f\u2e30\u2e31\u2e32\u2e33\u2e34\u2e35\u2e36\u2e37\u2e38\u2e39\u2e3a\u2e3b\u2e3c\u2e3d\u2e3e\u2e3f\u2e40\u2e41\u2e42\u2e43\u2e44\u2e45\u2e46\u2e47\u2e48\u2e49\u2e4a\u2e4b\u2e4c\u2e4d\u2e4e\u2e4f\u2e50\u2e51\u2e52\u2e53\u2e54\u2e55\u2e56\u2e57\u2e58\u2e59\u2e5a\u2e5b\u2e5c\u2e5d\u2e5e\u2e5f\u2e60\u2e61\u2e62\u2e63\u2e64\u2e65\u2e66\u2e67\u2e68\u2e69\u2e6a\u2e6b\u2e6c\u2e6d\u2e6e\u2e6f\u2e70\u2e71\u2e72\u2e73\u2e74\u2e75\u2e76\u2e77\u2e78\u2e79\u2e7a\u2e7b\u2e7c\u2e7d\u2e7e\u2e7f\u2e9a\u2ef4\u2ef5\u2ef6\u2ef7\u2ef8\u2ef9\u2efa\u2efb\u2efc\u2efd\u2efe\u2eff\u2fd6\u2fd7\u2fd8\u2fd9\u2fda\u2fdb\u2fdc\u2fdd\u2fde\u2fdf\u2fe0\u2fe1\u2fe2\u2fe3\u2fe4\u2fe5\u2fe6\u2fe7\u2fe8\u2fe9\u2fea\u2feb\u2fec\u2fed\u2fee\u2fef\u2ffc\u2ffd\u2ffe\u2fff\u3040\u3097\u3098\u3100\u3101\u3102\u3103\u3104\u312d\u312e\u312f\u3130\u318f\u31b8\u31b9\u31ba\u31bb\u31bc\u31bd\u31be\u31bf\u31d0\u31d1\u31d2\u31d3\u31d4\u31d5\u31d6\u31d7\u31d8\u31d9\u31da\u31db\u31dc\u31dd\u31de\u31df\u31e0\u31e1\u31e2\u31e3\u31e4\u31e5\u31e6\u31e7\u31e8\u31e9\u31ea\u31eb\u31ec\u31ed\u31ee\u31ef\u321f\u3244\u3245\u3246\u3247\u3248\u3249\u324a\u324b\u324c\u324d\u324e\u324f\u32ff\u4db6\u4db7\u4db8\u4db9\u4dba\u4dbb\u4dbc\u4dbd\u4dbe\u4dbf\u9fbc\u9fbd\u9fbe\u9fbf\u9fc0\u9fc1\u9fc2\u9fc3\u9fc4\u9fc5\u9fc6\u9fc7\u9fc8\u9fc9\u9fca\u9fcb\u9fcc\u9fcd\u9fce\u9fcf\u9fd0\u9fd1\u9fd2\u9fd3\u9fd4\u9fd5\u9fd6\u9fd7\u9fd8\u9fd9\u9fda\u9fdb\u9fdc\u9fdd\u9fde\u9fdf\u9fe0\u9fe1\u9fe2\u9fe3\u9fe4\u9fe5\u9fe6\u9fe7\u9fe8\u9fe9\u9fea\u9feb\u9fec\u9fed\u9fee\u9fef\u9ff0\u9ff1\u9ff2\u9ff3\u9ff4\u9ff5\u9ff6\u9ff7\u9ff8\u9ff9\u9ffa\u9ffb\u9ffc\u9ffd\u9ffe\u9fff\ua48d\ua48e\ua48f\ua4c7\ua4c8\ua4c9\ua4ca\ua4cb\ua4cc\ua4cd\ua4ce\ua4cf\ua4d0\ua4d1\ua4d2\ua4d3\ua4d4\ua4d5\ua4d6\ua4d7\ua4d8\ua4d9\ua4da\ua4db\ua4dc\ua4dd\ua4de\ua4df\ua4e0\ua4e1\ua4e2\ua4e3\ua4e4\ua4e5\ua4e6\ua4e7\ua4e8\ua4e9\ua4ea\ua4eb\ua4ec\ua4ed\ua4ee\ua4ef\ua4f0\ua4f1\ua4f2\ua4f3\ua4f4\ua4f5\ua4f6\ua4f7\ua4f8\ua4f9\ua4fa\ua4fb\ua4fc\ua4fd\ua4fe\ua4ff\ua500\ua501\ua502\ua503\ua504\ua505\ua506\ua507\ua508\ua509\ua50a\ua50b\ua50c\ua50d\ua50e\ua50f\ua510\ua511\ua512\ua513\ua514\ua515\ua516\ua517\ua518\ua519\ua51a\ua51b\ua51c\ua51d\ua51e\ua51f\ua520\ua521\ua522\ua523\ua524\ua525\ua526\ua527\ua528\ua529\ua52a\ua52b\ua52c\ua52d\ua52e\ua52f\ua530\ua531\ua532\ua533\ua534\ua535\ua536\ua537\ua538\ua539\ua53a\ua53b\ua53c\ua53d\ua53e\ua53f\ua540\ua541\ua542\ua543\ua544\ua545\ua546\ua547\ua548\ua549\ua54a\ua54b\ua54c\ua54d\ua54e\ua54f\ua550\ua551\ua552\ua553\ua554\ua555\ua556\ua557\ua558\ua559\ua55a\ua55b\ua55c\ua55d\ua55e\ua55f\ua560\ua561\ua562\ua563\ua564\ua565\ua566\ua567\ua568\ua569\ua56a\ua56b\ua56c\ua56d\ua56e\ua56f\ua570\ua571\ua572\ua573\ua574\ua575\ua576\ua577\ua578\ua579\ua57a\ua57b\ua57c\ua57d\ua57e\ua57f\ua580\ua581\ua582\ua583\ua584\ua585\ua586\ua587\ua588\ua589\ua58a\ua58b\ua58c\ua58d\ua58e\ua58f\ua590\ua591\ua592\ua593\ua594\ua595\ua596\ua597\ua598\ua599\ua59a\ua59b\ua59c\ua59d\ua59e\ua59f\ua5a0\ua5a1\ua5a2\ua5a3\ua5a4\ua5a5\ua5a6\ua5a7\ua5a8\ua5a9\ua5aa\ua5ab\ua5ac\ua5ad\ua5ae\ua5af\ua5b0\ua5b1\ua5b2\ua5b3\ua5b4\ua5b5\ua5b6\ua5b7\ua5b8\ua5b9\ua5ba\ua5bb\ua5bc\ua5bd\ua5be\ua5bf\ua5c0\ua5c1\ua5c2\ua5c3\ua5c4\ua5c5\ua5c6\ua5c7\ua5c8\ua5c9\ua5ca\ua5cb\ua5cc\ua5cd\ua5ce\ua5cf\ua5d0\ua5d1\ua5d2\ua5d3\ua5d4\ua5d5\ua5d6\ua5d7\ua5d8\ua5d9\ua5da\ua5db\ua5dc\ua5dd\ua5de\ua5df\ua5e0\ua5e1\ua5e2\ua5e3\ua5e4\ua5e5\ua5e6\ua5e7\ua5e8\ua5e9\ua5ea\ua5eb\ua5ec\ua5ed\ua5ee\ua5ef\ua5f0\ua5f1\ua5f2\ua5f3\ua5f4\ua5f5\ua5f6\ua5f7\ua5f8\ua5f9\ua5fa\ua5fb\ua5fc\ua5fd\ua5fe\ua5ff\ua600\ua601\ua602\ua603\ua604\ua605\ua606\ua607\ua608\ua609\ua60a\ua60b\ua60c\ua60d\ua60e\ua60f\ua610\ua611\ua612\ua613\ua614\ua615\ua616\ua617\ua618\ua619\ua61a\ua61b\ua61c\ua61d\ua61e\ua61f\ua620\ua621\ua622\ua623\ua624\ua625\ua626\ua627\ua628\ua629\ua62a\ua62b\ua62c\ua62d\ua62e\ua62f\ua630\ua631\ua632\ua633\ua634\ua635\ua636\ua637\ua638\ua639\ua63a\ua63b\ua63c\ua63d\ua63e\ua63f\ua640\ua641\ua642\ua643\ua644\ua645\ua646\ua647\ua648\ua649\ua64a\ua64b\ua64c\ua64d\ua64e\ua64f\ua650\ua651\ua652\ua653\ua654\ua655\ua656\ua657\ua658\ua659\ua65a\ua65b\ua65c\ua65d\ua65e\ua65f\ua660\ua661\ua662\ua663\ua664\ua665\ua666\ua667\ua668\ua669\ua66a\ua66b\ua66c\ua66d\ua66e\ua66f\ua670\ua671\ua672\ua673\ua674\ua675\ua676\ua677\ua678\ua679\ua67a\ua67b\ua67c\ua67d\ua67e\ua67f\ua680\ua681\ua682\ua683\ua684\ua685\ua686\ua687\ua688\ua689\ua68a\ua68b\ua68c\ua68d\ua68e\ua68f\ua690\ua691\ua692\ua693\ua694\ua695\ua696\ua697\ua698\ua699\ua69a\ua69b\ua69c\ua69d\ua69e\ua69f\ua6a0\ua6a1\ua6a2\ua6a3\ua6a4\ua6a5\ua6a6\ua6a7\ua6a8\ua6a9\ua6aa\ua6ab\ua6ac\ua6ad\ua6ae\ua6af\ua6b0\ua6b1\ua6b2\ua6b3\ua6b4\ua6b5\ua6b6\ua6b7\ua6b8\ua6b9\ua6ba\ua6bb\ua6bc\ua6bd\ua6be\ua6bf\ua6c0\ua6c1\ua6c2\ua6c3\ua6c4\ua6c5\ua6c6\ua6c7\ua6c8\ua6c9\ua6ca\ua6cb\ua6cc\ua6cd\ua6ce\ua6cf\ua6d0\ua6d1\ua6d2\ua6d3\ua6d4\ua6d5\ua6d6\ua6d7\ua6d8\ua6d9\ua6da\ua6db\ua6dc\ua6dd\ua6de\ua6df\ua6e0\ua6e1\ua6e2\ua6e3\ua6e4\ua6e5\ua6e6\ua6e7\ua6e8\ua6e9\ua6ea\ua6eb\ua6ec\ua6ed\ua6ee\ua6ef\ua6f0\ua6f1\ua6f2\ua6f3\ua6f4\ua6f5\ua6f6\ua6f7\ua6f8\ua6f9\ua6fa\ua6fb\ua6fc\ua6fd\ua6fe\ua6ff\ua717\ua718\ua719\ua71a\ua71b\ua71c\ua71d\ua71e\ua71f\ua720\ua721\ua722\ua723\ua724\ua725\ua726\ua727\ua728\ua729\ua72a\ua72b\ua72c\ua72d\ua72e\ua72f\ua730\ua731\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua739\ua73a\ua73b\ua73c\ua73d\ua73e\ua73f\ua740\ua741\ua742\ua743\ua744\ua745\ua746\ua747\ua748\ua749\ua74a\ua74b\ua74c\ua74d\ua74e\ua74f\ua750\ua751\ua752\ua753\ua754\ua755\ua756\ua757\ua758\ua759\ua75a\ua75b\ua75c\ua75d\ua75e\ua75f\ua760\ua761\ua762\ua763\ua764\ua765\ua766\ua767\ua768\ua769\ua76a\ua76b\ua76c\ua76d\ua76e\ua76f\ua770\ua771\ua772\ua773\ua774\ua775\ua776\ua777\ua778\ua779\ua77a\ua77b\ua77c\ua77d\ua77e\ua77f\ua780\ua781\ua782\ua783\ua784\ua785\ua786\ua787\ua788\ua789\ua78a\ua78b\ua78c\ua78d\ua78e\ua78f\ua790\ua791\ua792\ua793\ua794\ua795\ua796\ua797\ua798\ua799\ua79a\ua79b\ua79c\ua79d\ua79e\ua79f\ua7a0\ua7a1\ua7a2\ua7a3\ua7a4\ua7a5\ua7a6\ua7a7\ua7a8\ua7a9\ua7aa\ua7ab\ua7ac\ua7ad\ua7ae\ua7af\ua7b0\ua7b1\ua7b2\ua7b3\ua7b4\ua7b5\ua7b6\ua7b7\ua7b8\ua7b9\ua7ba\ua7bb\ua7bc\ua7bd\ua7be\ua7bf\ua7c0\ua7c1\ua7c2\ua7c3\ua7c4\ua7c5\ua7c6\ua7c7\ua7c8\ua7c9\ua7ca\ua7cb\ua7cc\ua7cd\ua7ce\ua7cf\ua7d0\ua7d1\ua7d2\ua7d3\ua7d4\ua7d5\ua7d6\ua7d7\ua7d8\ua7d9\ua7da\ua7db\ua7dc\ua7dd\ua7de\ua7df\ua7e0\ua7e1\ua7e2\ua7e3\ua7e4\ua7e5\ua7e6\ua7e7\ua7e8\ua7e9\ua7ea\ua7eb\ua7ec\ua7ed\ua7ee\ua7ef\ua7f0\ua7f1\ua7f2\ua7f3\ua7f4\ua7f5\ua7f6\ua7f7\ua7f8\ua7f9\ua7fa\ua7fb\ua7fc\ua7fd\ua7fe\ua7ff\ua82c\ua82d\ua82e\ua82f\ua830\ua831\ua832\ua833\ua834\ua835\ua836\ua837\ua838\ua839\ua83a\ua83b\ua83c\ua83d\ua83e\ua83f\ua840\ua841\ua842\ua843\ua844\ua845\ua846\ua847\ua848\ua849\ua84a\ua84b\ua84c\ua84d\ua84e\ua84f\ua850\ua851\ua852\ua853\ua854\ua855\ua856\ua857\ua858\ua859\ua85a\ua85b\ua85c\ua85d\ua85e\ua85f\ua860\ua861\ua862\ua863\ua864\ua865\ua866\ua867\ua868\ua869\ua86a\ua86b\ua86c\ua86d\ua86e\ua86f\ua870\ua871\ua872\ua873\ua874\ua875\ua876\ua877\ua878\ua879\ua87a\ua87b\ua87c\ua87d\ua87e\ua87f\ua880\ua881\ua882\ua883\ua884\ua885\ua886\ua887\ua888\ua889\ua88a\ua88b\ua88c\ua88d\ua88e\ua88f\ua890\ua891\ua892\ua893\ua894\ua895\ua896\ua897\ua898\ua899\ua89a\ua89b\ua89c\ua89d\ua89e\ua89f\ua8a0\ua8a1\ua8a2\ua8a3\ua8a4\ua8a5\ua8a6\ua8a7\ua8a8\ua8a9\ua8aa\ua8ab\ua8ac\ua8ad\ua8ae\ua8af\ua8b0\ua8b1\ua8b2\ua8b3\ua8b4\ua8b5\ua8b6\ua8b7\ua8b8\ua8b9\ua8ba\ua8bb\ua8bc\ua8bd\ua8be\ua8bf\ua8c0\ua8c1\ua8c2\ua8c3\ua8c4\ua8c5\ua8c6\ua8c7\ua8c8\ua8c9\ua8ca\ua8cb\ua8cc\ua8cd\ua8ce\ua8cf\ua8d0\ua8d1\ua8d2\ua8d3\ua8d4\ua8d5\ua8d6\ua8d7\ua8d8\ua8d9\ua8da\ua8db\ua8dc\ua8dd\ua8de\ua8df\ua8e0\ua8e1\ua8e2\ua8e3\ua8e4\ua8e5\ua8e6\ua8e7\ua8e8\ua8e9\ua8ea\ua8eb\ua8ec\ua8ed\ua8ee\ua8ef\ua8f0\ua8f1\ua8f2\ua8f3\ua8f4\ua8f5\ua8f6\ua8f7\ua8f8\ua8f9\ua8fa\ua8fb\ua8fc\ua8fd\ua8fe\ua8ff\ua900\ua901\ua902\ua903\ua904\ua905\ua906\ua907\ua908\ua909\ua90a\ua90b\ua90c\ua90d\ua90e\ua90f\ua910\ua911\ua912\ua913\ua914\ua915\ua916\ua917\ua918\ua919\ua91a\ua91b\ua91c\ua91d\ua91e\ua91f\ua920\ua921\ua922\ua923\ua924\ua925\ua926\ua927\ua928\ua929\ua92a\ua92b\ua92c\ua92d\ua92e\ua92f\ua930\ua931\ua932\ua933\ua934\ua935\ua936\ua937\ua938\ua939\ua93a\ua93b\ua93c\ua93d\ua93e\ua93f\ua940\ua941\ua942\ua943\ua944\ua945\ua946\ua947\ua948\ua949\ua94a\ua94b\ua94c\ua94d\ua94e\ua94f\ua950\ua951\ua952\ua953\ua954\ua955\ua956\ua957\ua958\ua959\ua95a\ua95b\ua95c\ua95d\ua95e\ua95f\ua960\ua961\ua962\ua963\ua964\ua965\ua966\ua967\ua968\ua969\ua96a\ua96b\ua96c\ua96d\ua96e\ua96f\ua970\ua971\ua972\ua973\ua974\ua975\ua976\ua977\ua978\ua979\ua97a\ua97b\ua97c\ua97d\ua97e\ua97f\ua980\ua981\ua982\ua983\ua984\ua985\ua986\ua987\ua988\ua989\ua98a\ua98b\ua98c\ua98d\ua98e\ua98f\ua990\ua991\ua992\ua993\ua994\ua995\ua996\ua997\ua998\ua999\ua99a\ua99b\ua99c\ua99d\ua99e\ua99f\ua9a0\ua9a1\ua9a2\ua9a3\ua9a4\ua9a5\ua9a6\ua9a7\ua9a8\ua9a9\ua9aa\ua9ab\ua9ac\ua9ad\ua9ae\ua9af\ua9b0\ua9b1\ua9b2\ua9b3\ua9b4\ua9b5\ua9b6\ua9b7\ua9b8\ua9b9\ua9ba\ua9bb\ua9bc\ua9bd\ua9be\ua9bf\ua9c0\ua9c1\ua9c2\ua9c3\ua9c4\ua9c5\ua9c6\ua9c7\ua9c8\ua9c9\ua9ca\ua9cb\ua9cc\ua9cd\ua9ce\ua9cf\ua9d0\ua9d1\ua9d2\ua9d3\ua9d4\ua9d5\ua9d6\ua9d7\ua9d8\ua9d9\ua9da\ua9db\ua9dc\ua9dd\ua9de\ua9df\ua9e0\ua9e1\ua9e2\ua9e3\ua9e4\ua9e5\ua9e6\ua9e7\ua9e8\ua9e9\ua9ea\ua9eb\ua9ec\ua9ed\ua9ee\ua9ef\ua9f0\ua9f1\ua9f2\ua9f3\ua9f4\ua9f5\ua9f6\ua9f7\ua9f8\ua9f9\ua9fa\ua9fb\ua9fc\ua9fd\ua9fe\ua9ff\uaa00\uaa01\uaa02\uaa03\uaa04\uaa05\uaa06\uaa07\uaa08\uaa09\uaa0a\uaa0b\uaa0c\uaa0d\uaa0e\uaa0f\uaa10\uaa11\uaa12\uaa13\uaa14\uaa15\uaa16\uaa17\uaa18\uaa19\uaa1a\uaa1b\uaa1c\uaa1d\uaa1e\uaa1f\uaa20\uaa21\uaa22\uaa23\uaa24\uaa25\uaa26\uaa27\uaa28\uaa29\uaa2a\uaa2b\uaa2c\uaa2d\uaa2e\uaa2f\uaa30\uaa31\uaa32\uaa33\uaa34\uaa35\uaa36\uaa37\uaa38\uaa39\uaa3a\uaa3b\uaa3c\uaa3d\uaa3e\uaa3f\uaa40\uaa41\uaa42\uaa43\uaa44\uaa45\uaa46\uaa47\uaa48\uaa49\uaa4a\uaa4b\uaa4c\uaa4d\uaa4e\uaa4f\uaa50\uaa51\uaa52\uaa53\uaa54\uaa55\uaa56\uaa57\uaa58\uaa59\uaa5a\uaa5b\uaa5c\uaa5d\uaa5e\uaa5f\uaa60\uaa61\uaa62\uaa63\uaa64\uaa65\uaa66\uaa67\uaa68\uaa69\uaa6a\uaa6b\uaa6c\uaa6d\uaa6e\uaa6f\uaa70\uaa71\uaa72\uaa73\uaa74\uaa75\uaa76\uaa77\uaa78\uaa79\uaa7a\uaa7b\uaa7c\uaa7d\uaa7e\uaa7f\uaa80\uaa81\uaa82\uaa83\uaa84\uaa85\uaa86\uaa87\uaa88\uaa89\uaa8a\uaa8b\uaa8c\uaa8d\uaa8e\uaa8f\uaa90\uaa91\uaa92\uaa93\uaa94\uaa95\uaa96\uaa97\uaa98\uaa99\uaa9a\uaa9b\uaa9c\uaa9d\uaa9e\uaa9f\uaaa0\uaaa1\uaaa2\uaaa3\uaaa4\uaaa5\uaaa6\uaaa7\uaaa8\uaaa9\uaaaa\uaaab\uaaac\uaaad\uaaae\uaaaf\uaab0\uaab1\uaab2\uaab3\uaab4\uaab5\uaab6\uaab7\uaab8\uaab9\uaaba\uaabb\uaabc\uaabd\uaabe\uaabf\uaac0\uaac1\uaac2\uaac3\uaac4\uaac5\uaac6\uaac7\uaac8\uaac9\uaaca\uaacb\uaacc\uaacd\uaace\uaacf\uaad0\uaad1\uaad2\uaad3\uaad4\uaad5\uaad6\uaad7\uaad8\uaad9\uaada\uaadb\uaadc\uaadd\uaade\uaadf\uaae0\uaae1\uaae2\uaae3\uaae4\uaae5\uaae6\uaae7\uaae8\uaae9\uaaea\uaaeb\uaaec\uaaed\uaaee\uaaef\uaaf0\uaaf1\uaaf2\uaaf3\uaaf4\uaaf5\uaaf6\uaaf7\uaaf8\uaaf9\uaafa\uaafb\uaafc\uaafd\uaafe\uaaff\uab00\uab01\uab02\uab03\uab04\uab05\uab06\uab07\uab08\uab09\uab0a\uab0b\uab0c\uab0d\uab0e\uab0f\uab10\uab11\uab12\uab13\uab14\uab15\uab16\uab17\uab18\uab19\uab1a\uab1b\uab1c\uab1d\uab1e\uab1f\uab20\uab21\uab22\uab23\uab24\uab25\uab26\uab27\uab28\uab29\uab2a\uab2b\uab2c\uab2d\uab2e\uab2f\uab30\uab31\uab32\uab33\uab34\uab35\uab36\uab37\uab38\uab39\uab3a\uab3b\uab3c\uab3d\uab3e\uab3f\uab40\uab41\uab42\uab43\uab44\uab45\uab46\uab47\uab48\uab49\uab4a\uab4b\uab4c\uab4d\uab4e\uab4f\uab50\uab51\uab52\uab53\uab54\uab55\uab56\uab57\uab58\uab59\uab5a\uab5b\uab5c\uab5d\uab5e\uab5f\uab60\uab61\uab62\uab63\uab64\uab65\uab66\uab67\uab68\uab69\uab6a\uab6b\uab6c\uab6d\uab6e\uab6f\uab70\uab71\uab72\uab73\uab74\uab75\uab76\uab77\uab78\uab79\uab7a\uab7b\uab7c\uab7d\uab7e\uab7f\uab80\uab81\uab82\uab83\uab84\uab85\uab86\uab87\uab88\uab89\uab8a\uab8b\uab8c\uab8d\uab8e\uab8f\uab90\uab91\uab92\uab93\uab94\uab95\uab96\uab97\uab98\uab99\uab9a\uab9b\uab9c\uab9d\uab9e\uab9f\uaba0\uaba1\uaba2\uaba3\uaba4\uaba5\uaba6\uaba7\uaba8\uaba9\uabaa\uabab\uabac\uabad\uabae\uabaf\uabb0\uabb1\uabb2\uabb3\uabb4\uabb5\uabb6\uabb7\uabb8\uabb9\uabba\uabbb\uabbc\uabbd\uabbe\uabbf\uabc0\uabc1\uabc2\uabc3\uabc4\uabc5\uabc6\uabc7\uabc8\uabc9\uabca\uabcb\uabcc\uabcd\uabce\uabcf\uabd0\uabd1\uabd2\uabd3\uabd4\uabd5\uabd6\uabd7\uabd8\uabd9\uabda\uabdb\uabdc\uabdd\uabde\uabdf\uabe0\uabe1\uabe2\uabe3\uabe4\uabe5\uabe6\uabe7\uabe8\uabe9\uabea\uabeb\uabec\uabed\uabee\uabef\uabf0\uabf1\uabf2\uabf3\uabf4\uabf5\uabf6\uabf7\uabf8\uabf9\uabfa\uabfb\uabfc\uabfd\uabfe\uabff\ud7a4\ud7a5\ud7a6\ud7a7\ud7a8\ud7a9\ud7aa\ud7ab\ud7ac\ud7ad\ud7ae\ud7af\ud7b0\ud7b1\ud7b2\ud7b3\ud7b4\ud7b5\ud7b6\ud7b7\ud7b8\ud7b9\ud7ba\ud7bb\ud7bc\ud7bd\ud7be\ud7bf\ud7c0\ud7c1\ud7c2\ud7c3\ud7c4\ud7c5\ud7c6\ud7c7\ud7c8\ud7c9\ud7ca\ud7cb\ud7cc\ud7cd\ud7ce\ud7cf\ud7d0\ud7d1\ud7d2\ud7d3\ud7d4\ud7d5\ud7d6\ud7d7\ud7d8\ud7d9\ud7da\ud7db\ud7dc\ud7dd\ud7de\ud7df\ud7e0\ud7e1\ud7e2\ud7e3\ud7e4\ud7e5\ud7e6\ud7e7\ud7e8\ud7e9\ud7ea\ud7eb\ud7ec\ud7ed\ud7ee\ud7ef\ud7f0\ud7f1\ud7f2\ud7f3\ud7f4\ud7f5\ud7f6\ud7f7\ud7f8\ud7f9\ud7fa\ud7fb\ud7fc\ud7fd\ud7fe\ud7ff\ufa2e\ufa2f\ufa6b\ufa6c\ufa6d\ufa6e\ufa6f\ufada\ufadb\ufadc\ufadd\ufade\ufadf\ufae0\ufae1\ufae2\ufae3\ufae4\ufae5\ufae6\ufae7\ufae8\ufae9\ufaea\ufaeb\ufaec\ufaed\ufaee\ufaef\ufaf0\ufaf1\ufaf2\ufaf3\ufaf4\ufaf5\ufaf6\ufaf7\ufaf8\ufaf9\ufafa\ufafb\ufafc\ufafd\ufafe\ufaff\ufb07\ufb08\ufb09\ufb0a\ufb0b\ufb0c\ufb0d\ufb0e\ufb0f\ufb10\ufb11\ufb12\ufb18\ufb19\ufb1a\ufb1b\ufb1c\ufb37\ufb3d\ufb3f\ufb42\ufb45\ufbb2\ufbb3\ufbb4\ufbb5\ufbb6\ufbb7\ufbb8\ufbb9\ufbba\ufbbb\ufbbc\ufbbd\ufbbe\ufbbf\ufbc0\ufbc1\ufbc2\ufbc3\ufbc4\ufbc5\ufbc6\ufbc7\ufbc8\ufbc9\ufbca\ufbcb\ufbcc\ufbcd\ufbce\ufbcf\ufbd0\ufbd1\ufbd2\ufd40\ufd41\ufd42\ufd43\ufd44\ufd45\ufd46\ufd47\ufd48\ufd49\ufd4a\ufd4b\ufd4c\ufd4d\ufd4e\ufd4f\ufd90\ufd91\ufdc8\ufdc9\ufdca\ufdcb\ufdcc\ufdcd\ufdce\ufdcf\ufdd0\ufdd1\ufdd2\ufdd3\ufdd4\ufdd5\ufdd6\ufdd7\ufdd8\ufdd9\ufdda\ufddb\ufddc\ufddd\ufdde\ufddf\ufde0\ufde1\ufde2\ufde3\ufde4\ufde5\ufde6\ufde7\ufde8\ufde9\ufdea\ufdeb\ufdec\ufded\ufdee\ufdef\ufdfe\ufdff\ufe1a\ufe1b\ufe1c\ufe1d\ufe1e\ufe1f\ufe24\ufe25\ufe26\ufe27\ufe28\ufe29\ufe2a\ufe2b\ufe2c\ufe2d\ufe2e\ufe2f\ufe53\ufe67\ufe6c\ufe6d\ufe6e\ufe6f\ufe75\ufefd\ufefe\uff00\uffbf\uffc0\uffc1\uffc8\uffc9\uffd0\uffd1\uffd8\uffd9\uffdd\uffde\uffdf\uffe7\uffef\ufff0\ufff1\ufff2\ufff3\ufff4\ufff5\ufff6\ufff7\ufff8\ufffe'
+
+Co = u'\ue000\ue001\ue002\ue003\ue004\ue005\ue006\ue007\ue008\ue009\ue00a\ue00b\ue00c\ue00d\ue00e\ue00f\ue010\ue011\ue012\ue013\ue014\ue015\ue016\ue017\ue018\ue019\ue01a\ue01b\ue01c\ue01d\ue01e\ue01f\ue020\ue021\ue022\ue023\ue024\ue025\ue026\ue027\ue028\ue029\ue02a\ue02b\ue02c\ue02d\ue02e\ue02f\ue030\ue031\ue032\ue033\ue034\ue035\ue036\ue037\ue038\ue039\ue03a\ue03b\ue03c\ue03d\ue03e\ue03f\ue040\ue041\ue042\ue043\ue044\ue045\ue046\ue047\ue048\ue049\ue04a\ue04b\ue04c\ue04d\ue04e\ue04f\ue050\ue051\ue052\ue053\ue054\ue055\ue056\ue057\ue058\ue059\ue05a\ue05b\ue05c\ue05d\ue05e\ue05f\ue060\ue061\ue062\ue063\ue064\ue065\ue066\ue067\ue068\ue069\ue06a\ue06b\ue06c\ue06d\ue06e\ue06f\ue070\ue071\ue072\ue073\ue074\ue075\ue076\ue077\ue078\ue079\ue07a\ue07b\ue07c\ue07d\ue07e\ue07f\ue080\ue081\ue082\ue083\ue084\ue085\ue086\ue087\ue088\ue089\ue08a\ue08b\ue08c\ue08d\ue08e\ue08f\ue090\ue091\ue092\ue093\ue094\ue095\ue096\ue097\ue098\ue099\ue09a\ue09b\ue09c\ue09d\ue09e\ue09f\ue0a0\ue0a1\ue0a2\ue0a3\ue0a4\ue0a5\ue0a6\ue0a7\ue0a8\ue0a9\ue0aa\ue0ab\ue0ac\ue0ad\ue0ae\ue0af\ue0b0\ue0b1\ue0b2\ue0b3\ue0b4\ue0b5\ue0b6\ue0b7\ue0b8\ue0b9\ue0ba\ue0bb\ue0bc\ue0bd\ue0be\ue0bf\ue0c0\ue0c1\ue0c2\ue0c3\ue0c4\ue0c5\ue0c6\ue0c7\ue0c8\ue0c9\ue0ca\ue0cb\ue0cc\ue0cd\ue0ce\ue0cf\ue0d0\ue0d1\ue0d2\ue0d3\ue0d4\ue0d5\ue0d6\ue0d7\ue0d8\ue0d9\ue0da\ue0db\ue0dc\ue0dd\ue0de\ue0df\ue0e0\ue0e1\ue0e2\ue0e3\ue0e4\ue0e5\ue0e6\ue0e7\ue0e8\ue0e9\ue0ea\ue0eb\ue0ec\ue0ed\ue0ee\ue0ef\ue0f0\ue0f1\ue0f2\ue0f3\ue0f4\ue0f5\ue0f6\ue0f7\ue0f8\ue0f9\ue0fa\ue0fb\ue0fc\ue0fd\ue0fe\ue0ff\ue100\ue101\ue102\ue103\ue104\ue105\ue106\ue107\ue108\ue109\ue10a\ue10b\ue10c\ue10d\ue10e\ue10f\ue110\ue111\ue112\ue113\ue114\ue115\ue116\ue117\ue118\ue119\ue11a\ue11b\ue11c\ue11d\ue11e\ue11f\ue120\ue121\ue122\ue123\ue124\ue125\ue126\ue127\ue128\ue129\ue12a\ue12b\ue12c\ue12d\ue12e\ue12f\ue130\ue131\ue132\ue133\ue134\ue135\ue136\ue137\ue138\ue139\ue13a\ue13b\ue13c\ue13d\ue13e\ue13f\ue140\ue141\ue142\ue143\ue144\ue145\ue146\ue147\ue148\ue149\ue14a\ue14b\ue14c\ue14d\ue14e\ue14f\ue150\ue151\ue152\ue153\ue154\ue155\ue156\ue157\ue158\ue159\ue15a\ue15b\ue15c\ue15d\ue15e\ue15f\ue160\ue161\ue162\ue163\ue164\ue165\ue166\ue167\ue168\ue169\ue16a\ue16b\ue16c\ue16d\ue16e\ue16f\ue170\ue171\ue172\ue173\ue174\ue175\ue176\ue177\ue178\ue179\ue17a\ue17b\ue17c\ue17d\ue17e\ue17f\ue180\ue181\ue182\ue183\ue184\ue185\ue186\ue187\ue188\ue189\ue18a\ue18b\ue18c\ue18d\ue18e\ue18f\ue190\ue191\ue192\ue193\ue194\ue195\ue196\ue197\ue198\ue199\ue19a\ue19b\ue19c\ue19d\ue19e\ue19f\ue1a0\ue1a1\ue1a2\ue1a3\ue1a4\ue1a5\ue1a6\ue1a7\ue1a8\ue1a9\ue1aa\ue1ab\ue1ac\ue1ad\ue1ae\ue1af\ue1b0\ue1b1\ue1b2\ue1b3\ue1b4\ue1b5\ue1b6\ue1b7\ue1b8\ue1b9\ue1ba\ue1bb\ue1bc\ue1bd\ue1be\ue1bf\ue1c0\ue1c1\ue1c2\ue1c3\ue1c4\ue1c5\ue1c6\ue1c7\ue1c8\ue1c9\ue1ca\ue1cb\ue1cc\ue1cd\ue1ce\ue1cf\ue1d0\ue1d1\ue1d2\ue1d3\ue1d4\ue1d5\ue1d6\ue1d7\ue1d8\ue1d9\ue1da\ue1db\ue1dc\ue1dd\ue1de\ue1df\ue1e0\ue1e1\ue1e2\ue1e3\ue1e4\ue1e5\ue1e6\ue1e7\ue1e8\ue1e9\ue1ea\ue1eb\ue1ec\ue1ed\ue1ee\ue1ef\ue1f0\ue1f1\ue1f2\ue1f3\ue1f4\ue1f5\ue1f6\ue1f7\ue1f8\ue1f9\ue1fa\ue1fb\ue1fc\ue1fd\ue1fe\ue1ff\ue200\ue201\ue202\ue203\ue204\ue205\ue206\ue207\ue208\ue209\ue20a\ue20b\ue20c\ue20d\ue20e\ue20f\ue210\ue211\ue212\ue213\ue214\ue215\ue216\ue217\ue218\ue219\ue21a\ue21b\ue21c\ue21d\ue21e\ue21f\ue220\ue221\ue222\ue223\ue224\ue225\ue226\ue227\ue228\ue229\ue22a\ue22b\ue22c\ue22d\ue22e\ue22f\ue230\ue231\ue232\ue233\ue234\ue235\ue236\ue237\ue238\ue239\ue23a\ue23b\ue23c\ue23d\ue23e\ue23f\ue240\ue241\ue242\ue243\ue244\ue245\ue246\ue247\ue248\ue249\ue24a\ue24b\ue24c\ue24d\ue24e\ue24f\ue250\ue251\ue252\ue253\ue254\ue255\ue256\ue257\ue258\ue259\ue25a\ue25b\ue25c\ue25d\ue25e\ue25f\ue260\ue261\ue262\ue263\ue264\ue265\ue266\ue267\ue268\ue269\ue26a\ue26b\ue26c\ue26d\ue26e\ue26f\ue270\ue271\ue272\ue273\ue274\ue275\ue276\ue277\ue278\ue279\ue27a\ue27b\ue27c\ue27d\ue27e\ue27f\ue280\ue281\ue282\ue283\ue284\ue285\ue286\ue287\ue288\ue289\ue28a\ue28b\ue28c\ue28d\ue28e\ue28f\ue290\ue291\ue292\ue293\ue294\ue295\ue296\ue297\ue298\ue299\ue29a\ue29b\ue29c\ue29d\ue29e\ue29f\ue2a0\ue2a1\ue2a2\ue2a3\ue2a4\ue2a5\ue2a6\ue2a7\ue2a8\ue2a9\ue2aa\ue2ab\ue2ac\ue2ad\ue2ae\ue2af\ue2b0\ue2b1\ue2b2\ue2b3\ue2b4\ue2b5\ue2b6\ue2b7\ue2b8\ue2b9\ue2ba\ue2bb\ue2bc\ue2bd\ue2be\ue2bf\ue2c0\ue2c1\ue2c2\ue2c3\ue2c4\ue2c5\ue2c6\ue2c7\ue2c8\ue2c9\ue2ca\ue2cb\ue2cc\ue2cd\ue2ce\ue2cf\ue2d0\ue2d1\ue2d2\ue2d3\ue2d4\ue2d5\ue2d6\ue2d7\ue2d8\ue2d9\ue2da\ue2db\ue2dc\ue2dd\ue2de\ue2df\ue2e0\ue2e1\ue2e2\ue2e3\ue2e4\ue2e5\ue2e6\ue2e7\ue2e8\ue2e9\ue2ea\ue2eb\ue2ec\ue2ed\ue2ee\ue2ef\ue2f0\ue2f1\ue2f2\ue2f3\ue2f4\ue2f5\ue2f6\ue2f7\ue2f8\ue2f9\ue2fa\ue2fb\ue2fc\ue2fd\ue2fe\ue2ff\ue300\ue301\ue302\ue303\ue304\ue305\ue306\ue307\ue308\ue309\ue30a\ue30b\ue30c\ue30d\ue30e\ue30f\ue310\ue311\ue312\ue313\ue314\ue315\ue316\ue317\ue318\ue319\ue31a\ue31b\ue31c\ue31d\ue31e\ue31f\ue320\ue321\ue322\ue323\ue324\ue325\ue326\ue327\ue328\ue329\ue32a\ue32b\ue32c\ue32d\ue32e\ue32f\ue330\ue331\ue332\ue333\ue334\ue335\ue336\ue337\ue338\ue339\ue33a\ue33b\ue33c\ue33d\ue33e\ue33f\ue340\ue341\ue342\ue343\ue344\ue345\ue346\ue347\ue348\ue349\ue34a\ue34b\ue34c\ue34d\ue34e\ue34f\ue350\ue351\ue352\ue353\ue354\ue355\ue356\ue357\ue358\ue359\ue35a\ue35b\ue35c\ue35d\ue35e\ue35f\ue360\ue361\ue362\ue363\ue364\ue365\ue366\ue367\ue368\ue369\ue36a\ue36b\ue36c\ue36d\ue36e\ue36f\ue370\ue371\ue372\ue373\ue374\ue375\ue376\ue377\ue378\ue379\ue37a\ue37b\ue37c\ue37d\ue37e\ue37f\ue380\ue381\ue382\ue383\ue384\ue385\ue386\ue387\ue388\ue389\ue38a\ue38b\ue38c\ue38d\ue38e\ue38f\ue390\ue391\ue392\ue393\ue394\ue395\ue396\ue397\ue398\ue399\ue39a\ue39b\ue39c\ue39d\ue39e\ue39f\ue3a0\ue3a1\ue3a2\ue3a3\ue3a4\ue3a5\ue3a6\ue3a7\ue3a8\ue3a9\ue3aa\ue3ab\ue3ac\ue3ad\ue3ae\ue3af\ue3b0\ue3b1\ue3b2\ue3b3\ue3b4\ue3b5\ue3b6\ue3b7\ue3b8\ue3b9\ue3ba\ue3bb\ue3bc\ue3bd\ue3be\ue3bf\ue3c0\ue3c1\ue3c2\ue3c3\ue3c4\ue3c5\ue3c6\ue3c7\ue3c8\ue3c9\ue3ca\ue3cb\ue3cc\ue3cd\ue3ce\ue3cf\ue3d0\ue3d1\ue3d2\ue3d3\ue3d4\ue3d5\ue3d6\ue3d7\ue3d8\ue3d9\ue3da\ue3db\ue3dc\ue3dd\ue3de\ue3df\ue3e0\ue3e1\ue3e2\ue3e3\ue3e4\ue3e5\ue3e6\ue3e7\ue3e8\ue3e9\ue3ea\ue3eb\ue3ec\ue3ed\ue3ee\ue3ef\ue3f0\ue3f1\ue3f2\ue3f3\ue3f4\ue3f5\ue3f6\ue3f7\ue3f8\ue3f9\ue3fa\ue3fb\ue3fc\ue3fd\ue3fe\ue3ff\ue400\ue401\ue402\ue403\ue404\ue405\ue406\ue407\ue408\ue409\ue40a\ue40b\ue40c\ue40d\ue40e\ue40f\ue410\ue411\ue412\ue413\ue414\ue415\ue416\ue417\ue418\ue419\ue41a\ue41b\ue41c\ue41d\ue41e\ue41f\ue420\ue421\ue422\ue423\ue424\ue425\ue426\ue427\ue428\ue429\ue42a\ue42b\ue42c\ue42d\ue42e\ue42f\ue430\ue431\ue432\ue433\ue434\ue435\ue436\ue437\ue438\ue439\ue43a\ue43b\ue43c\ue43d\ue43e\ue43f\ue440\ue441\ue442\ue443\ue444\ue445\ue446\ue447\ue448\ue449\ue44a\ue44b\ue44c\ue44d\ue44e\ue44f\ue450\ue451\ue452\ue453\ue454\ue455\ue456\ue457\ue458\ue459\ue45a\ue45b\ue45c\ue45d\ue45e\ue45f\ue460\ue461\ue462\ue463\ue464\ue465\ue466\ue467\ue468\ue469\ue46a\ue46b\ue46c\ue46d\ue46e\ue46f\ue470\ue471\ue472\ue473\ue474\ue475\ue476\ue477\ue478\ue479\ue47a\ue47b\ue47c\ue47d\ue47e\ue47f\ue480\ue481\ue482\ue483\ue484\ue485\ue486\ue487\ue488\ue489\ue48a\ue48b\ue48c\ue48d\ue48e\ue48f\ue490\ue491\ue492\ue493\ue494\ue495\ue496\ue497\ue498\ue499\ue49a\ue49b\ue49c\ue49d\ue49e\ue49f\ue4a0\ue4a1\ue4a2\ue4a3\ue4a4\ue4a5\ue4a6\ue4a7\ue4a8\ue4a9\ue4aa\ue4ab\ue4ac\ue4ad\ue4ae\ue4af\ue4b0\ue4b1\ue4b2\ue4b3\ue4b4\ue4b5\ue4b6\ue4b7\ue4b8\ue4b9\ue4ba\ue4bb\ue4bc\ue4bd\ue4be\ue4bf\ue4c0\ue4c1\ue4c2\ue4c3\ue4c4\ue4c5\ue4c6\ue4c7\ue4c8\ue4c9\ue4ca\ue4cb\ue4cc\ue4cd\ue4ce\ue4cf\ue4d0\ue4d1\ue4d2\ue4d3\ue4d4\ue4d5\ue4d6\ue4d7\ue4d8\ue4d9\ue4da\ue4db\ue4dc\ue4dd\ue4de\ue4df\ue4e0\ue4e1\ue4e2\ue4e3\ue4e4\ue4e5\ue4e6\ue4e7\ue4e8\ue4e9\ue4ea\ue4eb\ue4ec\ue4ed\ue4ee\ue4ef\ue4f0\ue4f1\ue4f2\ue4f3\ue4f4\ue4f5\ue4f6\ue4f7\ue4f8\ue4f9\ue4fa\ue4fb\ue4fc\ue4fd\ue4fe\ue4ff\ue500\ue501\ue502\ue503\ue504\ue505\ue506\ue507\ue508\ue509\ue50a\ue50b\ue50c\ue50d\ue50e\ue50f\ue510\ue511\ue512\ue513\ue514\ue515\ue516\ue517\ue518\ue519\ue51a\ue51b\ue51c\ue51d\ue51e\ue51f\ue520\ue521\ue522\ue523\ue524\ue525\ue526\ue527\ue528\ue529\ue52a\ue52b\ue52c\ue52d\ue52e\ue52f\ue530\ue531\ue532\ue533\ue534\ue535\ue536\ue537\ue538\ue539\ue53a\ue53b\ue53c\ue53d\ue53e\ue53f\ue540\ue541\ue542\ue543\ue544\ue545\ue546\ue547\ue548\ue549\ue54a\ue54b\ue54c\ue54d\ue54e\ue54f\ue550\ue551\ue552\ue553\ue554\ue555\ue556\ue557\ue558\ue559\ue55a\ue55b\ue55c\ue55d\ue55e\ue55f\ue560\ue561\ue562\ue563\ue564\ue565\ue566\ue567\ue568\ue569\ue56a\ue56b\ue56c\ue56d\ue56e\ue56f\ue570\ue571\ue572\ue573\ue574\ue575\ue576\ue577\ue578\ue579\ue57a\ue57b\ue57c\ue57d\ue57e\ue57f\ue580\ue581\ue582\ue583\ue584\ue585\ue586\ue587\ue588\ue589\ue58a\ue58b\ue58c\ue58d\ue58e\ue58f\ue590\ue591\ue592\ue593\ue594\ue595\ue596\ue597\ue598\ue599\ue59a\ue59b\ue59c\ue59d\ue59e\ue59f\ue5a0\ue5a1\ue5a2\ue5a3\ue5a4\ue5a5\ue5a6\ue5a7\ue5a8\ue5a9\ue5aa\ue5ab\ue5ac\ue5ad\ue5ae\ue5af\ue5b0\ue5b1\ue5b2\ue5b3\ue5b4\ue5b5\ue5b6\ue5b7\ue5b8\ue5b9\ue5ba\ue5bb\ue5bc\ue5bd\ue5be\ue5bf\ue5c0\ue5c1\ue5c2\ue5c3\ue5c4\ue5c5\ue5c6\ue5c7\ue5c8\ue5c9\ue5ca\ue5cb\ue5cc\ue5cd\ue5ce\ue5cf\ue5d0\ue5d1\ue5d2\ue5d3\ue5d4\ue5d5\ue5d6\ue5d7\ue5d8\ue5d9\ue5da\ue5db\ue5dc\ue5dd\ue5de\ue5df\ue5e0\ue5e1\ue5e2\ue5e3\ue5e4\ue5e5\ue5e6\ue5e7\ue5e8\ue5e9\ue5ea\ue5eb\ue5ec\ue5ed\ue5ee\ue5ef\ue5f0\ue5f1\ue5f2\ue5f3\ue5f4\ue5f5\ue5f6\ue5f7\ue5f8\ue5f9\ue5fa\ue5fb\ue5fc\ue5fd\ue5fe\ue5ff\ue600\ue601\ue602\ue603\ue604\ue605\ue606\ue607\ue608\ue609\ue60a\ue60b\ue60c\ue60d\ue60e\ue60f\ue610\ue611\ue612\ue613\ue614\ue615\ue616\ue617\ue618\ue619\ue61a\ue61b\ue61c\ue61d\ue61e\ue61f\ue620\ue621\ue622\ue623\ue624\ue625\ue626\ue627\ue628\ue629\ue62a\ue62b\ue62c\ue62d\ue62e\ue62f\ue630\ue631\ue632\ue633\ue634\ue635\ue636\ue637\ue638\ue639\ue63a\ue63b\ue63c\ue63d\ue63e\ue63f\ue640\ue641\ue642\ue643\ue644\ue645\ue646\ue647\ue648\ue649\ue64a\ue64b\ue64c\ue64d\ue64e\ue64f\ue650\ue651\ue652\ue653\ue654\ue655\ue656\ue657\ue658\ue659\ue65a\ue65b\ue65c\ue65d\ue65e\ue65f\ue660\ue661\ue662\ue663\ue664\ue665\ue666\ue667\ue668\ue669\ue66a\ue66b\ue66c\ue66d\ue66e\ue66f\ue670\ue671\ue672\ue673\ue674\ue675\ue676\ue677\ue678\ue679\ue67a\ue67b\ue67c\ue67d\ue67e\ue67f\ue680\ue681\ue682\ue683\ue684\ue685\ue686\ue687\ue688\ue689\ue68a\ue68b\ue68c\ue68d\ue68e\ue68f\ue690\ue691\ue692\ue693\ue694\ue695\ue696\ue697\ue698\ue699\ue69a\ue69b\ue69c\ue69d\ue69e\ue69f\ue6a0\ue6a1\ue6a2\ue6a3\ue6a4\ue6a5\ue6a6\ue6a7\ue6a8\ue6a9\ue6aa\ue6ab\ue6ac\ue6ad\ue6ae\ue6af\ue6b0\ue6b1\ue6b2\ue6b3\ue6b4\ue6b5\ue6b6\ue6b7\ue6b8\ue6b9\ue6ba\ue6bb\ue6bc\ue6bd\ue6be\ue6bf\ue6c0\ue6c1\ue6c2\ue6c3\ue6c4\ue6c5\ue6c6\ue6c7\ue6c8\ue6c9\ue6ca\ue6cb\ue6cc\ue6cd\ue6ce\ue6cf\ue6d0\ue6d1\ue6d2\ue6d3\ue6d4\ue6d5\ue6d6\ue6d7\ue6d8\ue6d9\ue6da\ue6db\ue6dc\ue6dd\ue6de\ue6df\ue6e0\ue6e1\ue6e2\ue6e3\ue6e4\ue6e5\ue6e6\ue6e7\ue6e8\ue6e9\ue6ea\ue6eb\ue6ec\ue6ed\ue6ee\ue6ef\ue6f0\ue6f1\ue6f2\ue6f3\ue6f4\ue6f5\ue6f6\ue6f7\ue6f8\ue6f9\ue6fa\ue6fb\ue6fc\ue6fd\ue6fe\ue6ff\ue700\ue701\ue702\ue703\ue704\ue705\ue706\ue707\ue708\ue709\ue70a\ue70b\ue70c\ue70d\ue70e\ue70f\ue710\ue711\ue712\ue713\ue714\ue715\ue716\ue717\ue718\ue719\ue71a\ue71b\ue71c\ue71d\ue71e\ue71f\ue720\ue721\ue722\ue723\ue724\ue725\ue726\ue727\ue728\ue729\ue72a\ue72b\ue72c\ue72d\ue72e\ue72f\ue730\ue731\ue732\ue733\ue734\ue735\ue736\ue737\ue738\ue739\ue73a\ue73b\ue73c\ue73d\ue73e\ue73f\ue740\ue741\ue742\ue743\ue744\ue745\ue746\ue747\ue748\ue749\ue74a\ue74b\ue74c\ue74d\ue74e\ue74f\ue750\ue751\ue752\ue753\ue754\ue755\ue756\ue757\ue758\ue759\ue75a\ue75b\ue75c\ue75d\ue75e\ue75f\ue760\ue761\ue762\ue763\ue764\ue765\ue766\ue767\ue768\ue769\ue76a\ue76b\ue76c\ue76d\ue76e\ue76f\ue770\ue771\ue772\ue773\ue774\ue775\ue776\ue777\ue778\ue779\ue77a\ue77b\ue77c\ue77d\ue77e\ue77f\ue780\ue781\ue782\ue783\ue784\ue785\ue786\ue787\ue788\ue789\ue78a\ue78b\ue78c\ue78d\ue78e\ue78f\ue790\ue791\ue792\ue793\ue794\ue795\ue796\ue797\ue798\ue799\ue79a\ue79b\ue79c\ue79d\ue79e\ue79f\ue7a0\ue7a1\ue7a2\ue7a3\ue7a4\ue7a5\ue7a6\ue7a7\ue7a8\ue7a9\ue7aa\ue7ab\ue7ac\ue7ad\ue7ae\ue7af\ue7b0\ue7b1\ue7b2\ue7b3\ue7b4\ue7b5\ue7b6\ue7b7\ue7b8\ue7b9\ue7ba\ue7bb\ue7bc\ue7bd\ue7be\ue7bf\ue7c0\ue7c1\ue7c2\ue7c3\ue7c4\ue7c5\ue7c6\ue7c7\ue7c8\ue7c9\ue7ca\ue7cb\ue7cc\ue7cd\ue7ce\ue7cf\ue7d0\ue7d1\ue7d2\ue7d3\ue7d4\ue7d5\ue7d6\ue7d7\ue7d8\ue7d9\ue7da\ue7db\ue7dc\ue7dd\ue7de\ue7df\ue7e0\ue7e1\ue7e2\ue7e3\ue7e4\ue7e5\ue7e6\ue7e7\ue7e8\ue7e9\ue7ea\ue7eb\ue7ec\ue7ed\ue7ee\ue7ef\ue7f0\ue7f1\ue7f2\ue7f3\ue7f4\ue7f5\ue7f6\ue7f7\ue7f8\ue7f9\ue7fa\ue7fb\ue7fc\ue7fd\ue7fe\ue7ff\ue800\ue801\ue802\ue803\ue804\ue805\ue806\ue807\ue808\ue809\ue80a\ue80b\ue80c\ue80d\ue80e\ue80f\ue810\ue811\ue812\ue813\ue814\ue815\ue816\ue817\ue818\ue819\ue81a\ue81b\ue81c\ue81d\ue81e\ue81f\ue820\ue821\ue822\ue823\ue824\ue825\ue826\ue827\ue828\ue829\ue82a\ue82b\ue82c\ue82d\ue82e\ue82f\ue830\ue831\ue832\ue833\ue834\ue835\ue836\ue837\ue838\ue839\ue83a\ue83b\ue83c\ue83d\ue83e\ue83f\ue840\ue841\ue842\ue843\ue844\ue845\ue846\ue847\ue848\ue849\ue84a\ue84b\ue84c\ue84d\ue84e\ue84f\ue850\ue851\ue852\ue853\ue854\ue855\ue856\ue857\ue858\ue859\ue85a\ue85b\ue85c\ue85d\ue85e\ue85f\ue860\ue861\ue862\ue863\ue864\ue865\ue866\ue867\ue868\ue869\ue86a\ue86b\ue86c\ue86d\ue86e\ue86f\ue870\ue871\ue872\ue873\ue874\ue875\ue876\ue877\ue878\ue879\ue87a\ue87b\ue87c\ue87d\ue87e\ue87f\ue880\ue881\ue882\ue883\ue884\ue885\ue886\ue887\ue888\ue889\ue88a\ue88b\ue88c\ue88d\ue88e\ue88f\ue890\ue891\ue892\ue893\ue894\ue895\ue896\ue897\ue898\ue899\ue89a\ue89b\ue89c\ue89d\ue89e\ue89f\ue8a0\ue8a1\ue8a2\ue8a3\ue8a4\ue8a5\ue8a6\ue8a7\ue8a8\ue8a9\ue8aa\ue8ab\ue8ac\ue8ad\ue8ae\ue8af\ue8b0\ue8b1\ue8b2\ue8b3\ue8b4\ue8b5\ue8b6\ue8b7\ue8b8\ue8b9\ue8ba\ue8bb\ue8bc\ue8bd\ue8be\ue8bf\ue8c0\ue8c1\ue8c2\ue8c3\ue8c4\ue8c5\ue8c6\ue8c7\ue8c8\ue8c9\ue8ca\ue8cb\ue8cc\ue8cd\ue8ce\ue8cf\ue8d0\ue8d1\ue8d2\ue8d3\ue8d4\ue8d5\ue8d6\ue8d7\ue8d8\ue8d9\ue8da\ue8db\ue8dc\ue8dd\ue8de\ue8df\ue8e0\ue8e1\ue8e2\ue8e3\ue8e4\ue8e5\ue8e6\ue8e7\ue8e8\ue8e9\ue8ea\ue8eb\ue8ec\ue8ed\ue8ee\ue8ef\ue8f0\ue8f1\ue8f2\ue8f3\ue8f4\ue8f5\ue8f6\ue8f7\ue8f8\ue8f9\ue8fa\ue8fb\ue8fc\ue8fd\ue8fe\ue8ff\ue900\ue901\ue902\ue903\ue904\ue905\ue906\ue907\ue908\ue909\ue90a\ue90b\ue90c\ue90d\ue90e\ue90f\ue910\ue911\ue912\ue913\ue914\ue915\ue916\ue917\ue918\ue919\ue91a\ue91b\ue91c\ue91d\ue91e\ue91f\ue920\ue921\ue922\ue923\ue924\ue925\ue926\ue927\ue928\ue929\ue92a\ue92b\ue92c\ue92d\ue92e\ue92f\ue930\ue931\ue932\ue933\ue934\ue935\ue936\ue937\ue938\ue939\ue93a\ue93b\ue93c\ue93d\ue93e\ue93f\ue940\ue941\ue942\ue943\ue944\ue945\ue946\ue947\ue948\ue949\ue94a\ue94b\ue94c\ue94d\ue94e\ue94f\ue950\ue951\ue952\ue953\ue954\ue955\ue956\ue957\ue958\ue959\ue95a\ue95b\ue95c\ue95d\ue95e\ue95f\ue960\ue961\ue962\ue963\ue964\ue965\ue966\ue967\ue968\ue969\ue96a\ue96b\ue96c\ue96d\ue96e\ue96f\ue970\ue971\ue972\ue973\ue974\ue975\ue976\ue977\ue978\ue979\ue97a\ue97b\ue97c\ue97d\ue97e\ue97f\ue980\ue981\ue982\ue983\ue984\ue985\ue986\ue987\ue988\ue989\ue98a\ue98b\ue98c\ue98d\ue98e\ue98f\ue990\ue991\ue992\ue993\ue994\ue995\ue996\ue997\ue998\ue999\ue99a\ue99b\ue99c\ue99d\ue99e\ue99f\ue9a0\ue9a1\ue9a2\ue9a3\ue9a4\ue9a5\ue9a6\ue9a7\ue9a8\ue9a9\ue9aa\ue9ab\ue9ac\ue9ad\ue9ae\ue9af\ue9b0\ue9b1\ue9b2\ue9b3\ue9b4\ue9b5\ue9b6\ue9b7\ue9b8\ue9b9\ue9ba\ue9bb\ue9bc\ue9bd\ue9be\ue9bf\ue9c0\ue9c1\ue9c2\ue9c3\ue9c4\ue9c5\ue9c6\ue9c7\ue9c8\ue9c9\ue9ca\ue9cb\ue9cc\ue9cd\ue9ce\ue9cf\ue9d0\ue9d1\ue9d2\ue9d3\ue9d4\ue9d5\ue9d6\ue9d7\ue9d8\ue9d9\ue9da\ue9db\ue9dc\ue9dd\ue9de\ue9df\ue9e0\ue9e1\ue9e2\ue9e3\ue9e4\ue9e5\ue9e6\ue9e7\ue9e8\ue9e9\ue9ea\ue9eb\ue9ec\ue9ed\ue9ee\ue9ef\ue9f0\ue9f1\ue9f2\ue9f3\ue9f4\ue9f5\ue9f6\ue9f7\ue9f8\ue9f9\ue9fa\ue9fb\ue9fc\ue9fd\ue9fe\ue9ff\uea00\uea01\uea02\uea03\uea04\uea05\uea06\uea07\uea08\uea09\uea0a\uea0b\uea0c\uea0d\uea0e\uea0f\uea10\uea11\uea12\uea13\uea14\uea15\uea16\uea17\uea18\uea19\uea1a\uea1b\uea1c\uea1d\uea1e\uea1f\uea20\uea21\uea22\uea23\uea24\uea25\uea26\uea27\uea28\uea29\uea2a\uea2b\uea2c\uea2d\uea2e\uea2f\uea30\uea31\uea32\uea33\uea34\uea35\uea36\uea37\uea38\uea39\uea3a\uea3b\uea3c\uea3d\uea3e\uea3f\uea40\uea41\uea42\uea43\uea44\uea45\uea46\uea47\uea48\uea49\uea4a\uea4b\uea4c\uea4d\uea4e\uea4f\uea50\uea51\uea52\uea53\uea54\uea55\uea56\uea57\uea58\uea59\uea5a\uea5b\uea5c\uea5d\uea5e\uea5f\uea60\uea61\uea62\uea63\uea64\uea65\uea66\uea67\uea68\uea69\uea6a\uea6b\uea6c\uea6d\uea6e\uea6f\uea70\uea71\uea72\uea73\uea74\uea75\uea76\uea77\uea78\uea79\uea7a\uea7b\uea7c\uea7d\uea7e\uea7f\uea80\uea81\uea82\uea83\uea84\uea85\uea86\uea87\uea88\uea89\uea8a\uea8b\uea8c\uea8d\uea8e\uea8f\uea90\uea91\uea92\uea93\uea94\uea95\uea96\uea97\uea98\uea99\uea9a\uea9b\uea9c\uea9d\uea9e\uea9f\ueaa0\ueaa1\ueaa2\ueaa3\ueaa4\ueaa5\ueaa6\ueaa7\ueaa8\ueaa9\ueaaa\ueaab\ueaac\ueaad\ueaae\ueaaf\ueab0\ueab1\ueab2\ueab3\ueab4\ueab5\ueab6\ueab7\ueab8\ueab9\ueaba\ueabb\ueabc\ueabd\ueabe\ueabf\ueac0\ueac1\ueac2\ueac3\ueac4\ueac5\ueac6\ueac7\ueac8\ueac9\ueaca\ueacb\ueacc\ueacd\ueace\ueacf\uead0\uead1\uead2\uead3\uead4\uead5\uead6\uead7\uead8\uead9\ueada\ueadb\ueadc\ueadd\ueade\ueadf\ueae0\ueae1\ueae2\ueae3\ueae4\ueae5\ueae6\ueae7\ueae8\ueae9\ueaea\ueaeb\ueaec\ueaed\ueaee\ueaef\ueaf0\ueaf1\ueaf2\ueaf3\ueaf4\ueaf5\ueaf6\ueaf7\ueaf8\ueaf9\ueafa\ueafb\ueafc\ueafd\ueafe\ueaff\ueb00\ueb01\ueb02\ueb03\ueb04\ueb05\ueb06\ueb07\ueb08\ueb09\ueb0a\ueb0b\ueb0c\ueb0d\ueb0e\ueb0f\ueb10\ueb11\ueb12\ueb13\ueb14\ueb15\ueb16\ueb17\ueb18\ueb19\ueb1a\ueb1b\ueb1c\ueb1d\ueb1e\ueb1f\ueb20\ueb21\ueb22\ueb23\ueb24\ueb25\ueb26\ueb27\ueb28\ueb29\ueb2a\ueb2b\ueb2c\ueb2d\ueb2e\ueb2f\ueb30\ueb31\ueb32\ueb33\ueb34\ueb35\ueb36\ueb37\ueb38\ueb39\ueb3a\ueb3b\ueb3c\ueb3d\ueb3e\ueb3f\ueb40\ueb41\ueb42\ueb43\ueb44\ueb45\ueb46\ueb47\ueb48\ueb49\ueb4a\ueb4b\ueb4c\ueb4d\ueb4e\ueb4f\ueb50\ueb51\ueb52\ueb53\ueb54\ueb55\ueb56\ueb57\ueb58\ueb59\ueb5a\ueb5b\ueb5c\ueb5d\ueb5e\ueb5f\ueb60\ueb61\ueb62\ueb63\ueb64\ueb65\ueb66\ueb67\ueb68\ueb69\ueb6a\ueb6b\ueb6c\ueb6d\ueb6e\ueb6f\ueb70\ueb71\ueb72\ueb73\ueb74\ueb75\ueb76\ueb77\ueb78\ueb79\ueb7a\ueb7b\ueb7c\ueb7d\ueb7e\ueb7f\ueb80\ueb81\ueb82\ueb83\ueb84\ueb85\ueb86\ueb87\ueb88\ueb89\ueb8a\ueb8b\ueb8c\ueb8d\ueb8e\ueb8f\ueb90\ueb91\ueb92\ueb93\ueb94\ueb95\ueb96\ueb97\ueb98\ueb99\ueb9a\ueb9b\ueb9c\ueb9d\ueb9e\ueb9f\ueba0\ueba1\ueba2\ueba3\ueba4\ueba5\ueba6\ueba7\ueba8\ueba9\uebaa\uebab\uebac\uebad\uebae\uebaf\uebb0\uebb1\uebb2\uebb3\uebb4\uebb5\uebb6\uebb7\uebb8\uebb9\uebba\uebbb\uebbc\uebbd\uebbe\uebbf\uebc0\uebc1\uebc2\uebc3\uebc4\uebc5\uebc6\uebc7\uebc8\uebc9\uebca\uebcb\uebcc\uebcd\uebce\uebcf\uebd0\uebd1\uebd2\uebd3\uebd4\uebd5\uebd6\uebd7\uebd8\uebd9\uebda\uebdb\uebdc\uebdd\uebde\uebdf\uebe0\uebe1\uebe2\uebe3\uebe4\uebe5\uebe6\uebe7\uebe8\uebe9\uebea\uebeb\uebec\uebed\uebee\uebef\uebf0\uebf1\uebf2\uebf3\uebf4\uebf5\uebf6\uebf7\uebf8\uebf9\uebfa\uebfb\uebfc\uebfd\uebfe\uebff\uec00\uec01\uec02\uec03\uec04\uec05\uec06\uec07\uec08\uec09\uec0a\uec0b\uec0c\uec0d\uec0e\uec0f\uec10\uec11\uec12\uec13\uec14\uec15\uec16\uec17\uec18\uec19\uec1a\uec1b\uec1c\uec1d\uec1e\uec1f\uec20\uec21\uec22\uec23\uec24\uec25\uec26\uec27\uec28\uec29\uec2a\uec2b\uec2c\uec2d\uec2e\uec2f\uec30\uec31\uec32\uec33\uec34\uec35\uec36\uec37\uec38\uec39\uec3a\uec3b\uec3c\uec3d\uec3e\uec3f\uec40\uec41\uec42\uec43\uec44\uec45\uec46\uec47\uec48\uec49\uec4a\uec4b\uec4c\uec4d\uec4e\uec4f\uec50\uec51\uec52\uec53\uec54\uec55\uec56\uec57\uec58\uec59\uec5a\uec5b\uec5c\uec5d\uec5e\uec5f\uec60\uec61\uec62\uec63\uec64\uec65\uec66\uec67\uec68\uec69\uec6a\uec6b\uec6c\uec6d\uec6e\uec6f\uec70\uec71\uec72\uec73\uec74\uec75\uec76\uec77\uec78\uec79\uec7a\uec7b\uec7c\uec7d\uec7e\uec7f\uec80\uec81\uec82\uec83\uec84\uec85\uec86\uec87\uec88\uec89\uec8a\uec8b\uec8c\uec8d\uec8e\uec8f\uec90\uec91\uec92\uec93\uec94\uec95\uec96\uec97\uec98\uec99\uec9a\uec9b\uec9c\uec9d\uec9e\uec9f\ueca0\ueca1\ueca2\ueca3\ueca4\ueca5\ueca6\ueca7\ueca8\ueca9\uecaa\uecab\uecac\uecad\uecae\uecaf\uecb0\uecb1\uecb2\uecb3\uecb4\uecb5\uecb6\uecb7\uecb8\uecb9\uecba\uecbb\uecbc\uecbd\uecbe\uecbf\uecc0\uecc1\uecc2\uecc3\uecc4\uecc5\uecc6\uecc7\uecc8\uecc9\uecca\ueccb\ueccc\ueccd\uecce\ueccf\uecd0\uecd1\uecd2\uecd3\uecd4\uecd5\uecd6\uecd7\uecd8\uecd9\uecda\uecdb\uecdc\uecdd\uecde\uecdf\uece0\uece1\uece2\uece3\uece4\uece5\uece6\uece7\uece8\uece9\uecea\ueceb\uecec\ueced\uecee\uecef\uecf0\uecf1\uecf2\uecf3\uecf4\uecf5\uecf6\uecf7\uecf8\uecf9\uecfa\uecfb\uecfc\uecfd\uecfe\uecff\ued00\ued01\ued02\ued03\ued04\ued05\ued06\ued07\ued08\ued09\ued0a\ued0b\ued0c\ued0d\ued0e\ued0f\ued10\ued11\ued12\ued13\ued14\ued15\ued16\ued17\ued18\ued19\ued1a\ued1b\ued1c\ued1d\ued1e\ued1f\ued20\ued21\ued22\ued23\ued24\ued25\ued26\ued27\ued28\ued29\ued2a\ued2b\ued2c\ued2d\ued2e\ued2f\ued30\ued31\ued32\ued33\ued34\ued35\ued36\ued37\ued38\ued39\ued3a\ued3b\ued3c\ued3d\ued3e\ued3f\ued40\ued41\ued42\ued43\ued44\ued45\ued46\ued47\ued48\ued49\ued4a\ued4b\ued4c\ued4d\ued4e\ued4f\ued50\ued51\ued52\ued53\ued54\ued55\ued56\ued57\ued58\ued59\ued5a\ued5b\ued5c\ued5d\ued5e\ued5f\ued60\ued61\ued62\ued63\ued64\ued65\ued66\ued67\ued68\ued69\ued6a\ued6b\ued6c\ued6d\ued6e\ued6f\ued70\ued71\ued72\ued73\ued74\ued75\ued76\ued77\ued78\ued79\ued7a\ued7b\ued7c\ued7d\ued7e\ued7f\ued80\ued81\ued82\ued83\ued84\ued85\ued86\ued87\ued88\ued89\ued8a\ued8b\ued8c\ued8d\ued8e\ued8f\ued90\ued91\ued92\ued93\ued94\ued95\ued96\ued97\ued98\ued99\ued9a\ued9b\ued9c\ued9d\ued9e\ued9f\ueda0\ueda1\ueda2\ueda3\ueda4\ueda5\ueda6\ueda7\ueda8\ueda9\uedaa\uedab\uedac\uedad\uedae\uedaf\uedb0\uedb1\uedb2\uedb3\uedb4\uedb5\uedb6\uedb7\uedb8\uedb9\uedba\uedbb\uedbc\uedbd\uedbe\uedbf\uedc0\uedc1\uedc2\uedc3\uedc4\uedc5\uedc6\uedc7\uedc8\uedc9\uedca\uedcb\uedcc\uedcd\uedce\uedcf\uedd0\uedd1\uedd2\uedd3\uedd4\uedd5\uedd6\uedd7\uedd8\uedd9\uedda\ueddb\ueddc\ueddd\uedde\ueddf\uede0\uede1\uede2\uede3\uede4\uede5\uede6\uede7\uede8\uede9\uedea\uedeb\uedec\ueded\uedee\uedef\uedf0\uedf1\uedf2\uedf3\uedf4\uedf5\uedf6\uedf7\uedf8\uedf9\uedfa\uedfb\uedfc\uedfd\uedfe\uedff\uee00\uee01\uee02\uee03\uee04\uee05\uee06\uee07\uee08\uee09\uee0a\uee0b\uee0c\uee0d\uee0e\uee0f\uee10\uee11\uee12\uee13\uee14\uee15\uee16\uee17\uee18\uee19\uee1a\uee1b\uee1c\uee1d\uee1e\uee1f\uee20\uee21\uee22\uee23\uee24\uee25\uee26\uee27\uee28\uee29\uee2a\uee2b\uee2c\uee2d\uee2e\uee2f\uee30\uee31\uee32\uee33\uee34\uee35\uee36\uee37\uee38\uee39\uee3a\uee3b\uee3c\uee3d\uee3e\uee3f\uee40\uee41\uee42\uee43\uee44\uee45\uee46\uee47\uee48\uee49\uee4a\uee4b\uee4c\uee4d\uee4e\uee4f\uee50\uee51\uee52\uee53\uee54\uee55\uee56\uee57\uee58\uee59\uee5a\uee5b\uee5c\uee5d\uee5e\uee5f\uee60\uee61\uee62\uee63\uee64\uee65\uee66\uee67\uee68\uee69\uee6a\uee6b\uee6c\uee6d\uee6e\uee6f\uee70\uee71\uee72\uee73\uee74\uee75\uee76\uee77\uee78\uee79\uee7a\uee7b\uee7c\uee7d\uee7e\uee7f\uee80\uee81\uee82\uee83\uee84\uee85\uee86\uee87\uee88\uee89\uee8a\uee8b\uee8c\uee8d\uee8e\uee8f\uee90\uee91\uee92\uee93\uee94\uee95\uee96\uee97\uee98\uee99\uee9a\uee9b\uee9c\uee9d\uee9e\uee9f\ueea0\ueea1\ueea2\ueea3\ueea4\ueea5\ueea6\ueea7\ueea8\ueea9\ueeaa\ueeab\ueeac\ueead\ueeae\ueeaf\ueeb0\ueeb1\ueeb2\ueeb3\ueeb4\ueeb5\ueeb6\ueeb7\ueeb8\ueeb9\ueeba\ueebb\ueebc\ueebd\ueebe\ueebf\ueec0\ueec1\ueec2\ueec3\ueec4\ueec5\ueec6\ueec7\ueec8\ueec9\ueeca\ueecb\ueecc\ueecd\ueece\ueecf\ueed0\ueed1\ueed2\ueed3\ueed4\ueed5\ueed6\ueed7\ueed8\ueed9\ueeda\ueedb\ueedc\ueedd\ueede\ueedf\ueee0\ueee1\ueee2\ueee3\ueee4\ueee5\ueee6\ueee7\ueee8\ueee9\ueeea\ueeeb\ueeec\ueeed\ueeee\ueeef\ueef0\ueef1\ueef2\ueef3\ueef4\ueef5\ueef6\ueef7\ueef8\ueef9\ueefa\ueefb\ueefc\ueefd\ueefe\ueeff\uef00\uef01\uef02\uef03\uef04\uef05\uef06\uef07\uef08\uef09\uef0a\uef0b\uef0c\uef0d\uef0e\uef0f\uef10\uef11\uef12\uef13\uef14\uef15\uef16\uef17\uef18\uef19\uef1a\uef1b\uef1c\uef1d\uef1e\uef1f\uef20\uef21\uef22\uef23\uef24\uef25\uef26\uef27\uef28\uef29\uef2a\uef2b\uef2c\uef2d\uef2e\uef2f\uef30\uef31\uef32\uef33\uef34\uef35\uef36\uef37\uef38\uef39\uef3a\uef3b\uef3c\uef3d\uef3e\uef3f\uef40\uef41\uef42\uef43\uef44\uef45\uef46\uef47\uef48\uef49\uef4a\uef4b\uef4c\uef4d\uef4e\uef4f\uef50\uef51\uef52\uef53\uef54\uef55\uef56\uef57\uef58\uef59\uef5a\uef5b\uef5c\uef5d\uef5e\uef5f\uef60\uef61\uef62\uef63\uef64\uef65\uef66\uef67\uef68\uef69\uef6a\uef6b\uef6c\uef6d\uef6e\uef6f\uef70\uef71\uef72\uef73\uef74\uef75\uef76\uef77\uef78\uef79\uef7a\uef7b\uef7c\uef7d\uef7e\uef7f\uef80\uef81\uef82\uef83\uef84\uef85\uef86\uef87\uef88\uef89\uef8a\uef8b\uef8c\uef8d\uef8e\uef8f\uef90\uef91\uef92\uef93\uef94\uef95\uef96\uef97\uef98\uef99\uef9a\uef9b\uef9c\uef9d\uef9e\uef9f\uefa0\uefa1\uefa2\uefa3\uefa4\uefa5\uefa6\uefa7\uefa8\uefa9\uefaa\uefab\uefac\uefad\uefae\uefaf\uefb0\uefb1\uefb2\uefb3\uefb4\uefb5\uefb6\uefb7\uefb8\uefb9\uefba\uefbb\uefbc\uefbd\uefbe\uefbf\uefc0\uefc1\uefc2\uefc3\uefc4\uefc5\uefc6\uefc7\uefc8\uefc9\uefca\uefcb\uefcc\uefcd\uefce\uefcf\uefd0\uefd1\uefd2\uefd3\uefd4\uefd5\uefd6\uefd7\uefd8\uefd9\uefda\uefdb\uefdc\uefdd\uefde\uefdf\uefe0\uefe1\uefe2\uefe3\uefe4\uefe5\uefe6\uefe7\uefe8\uefe9\uefea\uefeb\uefec\uefed\uefee\uefef\ueff0\ueff1\ueff2\ueff3\ueff4\ueff5\ueff6\ueff7\ueff8\ueff9\ueffa\ueffb\ueffc\ueffd\ueffe\uefff\uf000\uf001\uf002\uf003\uf004\uf005\uf006\uf007\uf008\uf009\uf00a\uf00b\uf00c\uf00d\uf00e\uf00f\uf010\uf011\uf012\uf013\uf014\uf015\uf016\uf017\uf018\uf019\uf01a\uf01b\uf01c\uf01d\uf01e\uf01f\uf020\uf021\uf022\uf023\uf024\uf025\uf026\uf027\uf028\uf029\uf02a\uf02b\uf02c\uf02d\uf02e\uf02f\uf030\uf031\uf032\uf033\uf034\uf035\uf036\uf037\uf038\uf039\uf03a\uf03b\uf03c\uf03d\uf03e\uf03f\uf040\uf041\uf042\uf043\uf044\uf045\uf046\uf047\uf048\uf049\uf04a\uf04b\uf04c\uf04d\uf04e\uf04f\uf050\uf051\uf052\uf053\uf054\uf055\uf056\uf057\uf058\uf059\uf05a\uf05b\uf05c\uf05d\uf05e\uf05f\uf060\uf061\uf062\uf063\uf064\uf065\uf066\uf067\uf068\uf069\uf06a\uf06b\uf06c\uf06d\uf06e\uf06f\uf070\uf071\uf072\uf073\uf074\uf075\uf076\uf077\uf078\uf079\uf07a\uf07b\uf07c\uf07d\uf07e\uf07f\uf080\uf081\uf082\uf083\uf084\uf085\uf086\uf087\uf088\uf089\uf08a\uf08b\uf08c\uf08d\uf08e\uf08f\uf090\uf091\uf092\uf093\uf094\uf095\uf096\uf097\uf098\uf099\uf09a\uf09b\uf09c\uf09d\uf09e\uf09f\uf0a0\uf0a1\uf0a2\uf0a3\uf0a4\uf0a5\uf0a6\uf0a7\uf0a8\uf0a9\uf0aa\uf0ab\uf0ac\uf0ad\uf0ae\uf0af\uf0b0\uf0b1\uf0b2\uf0b3\uf0b4\uf0b5\uf0b6\uf0b7\uf0b8\uf0b9\uf0ba\uf0bb\uf0bc\uf0bd\uf0be\uf0bf\uf0c0\uf0c1\uf0c2\uf0c3\uf0c4\uf0c5\uf0c6\uf0c7\uf0c8\uf0c9\uf0ca\uf0cb\uf0cc\uf0cd\uf0ce\uf0cf\uf0d0\uf0d1\uf0d2\uf0d3\uf0d4\uf0d5\uf0d6\uf0d7\uf0d8\uf0d9\uf0da\uf0db\uf0dc\uf0dd\uf0de\uf0df\uf0e0\uf0e1\uf0e2\uf0e3\uf0e4\uf0e5\uf0e6\uf0e7\uf0e8\uf0e9\uf0ea\uf0eb\uf0ec\uf0ed\uf0ee\uf0ef\uf0f0\uf0f1\uf0f2\uf0f3\uf0f4\uf0f5\uf0f6\uf0f7\uf0f8\uf0f9\uf0fa\uf0fb\uf0fc\uf0fd\uf0fe\uf0ff\uf100\uf101\uf102\uf103\uf104\uf105\uf106\uf107\uf108\uf109\uf10a\uf10b\uf10c\uf10d\uf10e\uf10f\uf110\uf111\uf112\uf113\uf114\uf115\uf116\uf117\uf118\uf119\uf11a\uf11b\uf11c\uf11d\uf11e\uf11f\uf120\uf121\uf122\uf123\uf124\uf125\uf126\uf127\uf128\uf129\uf12a\uf12b\uf12c\uf12d\uf12e\uf12f\uf130\uf131\uf132\uf133\uf134\uf135\uf136\uf137\uf138\uf139\uf13a\uf13b\uf13c\uf13d\uf13e\uf13f\uf140\uf141\uf142\uf143\uf144\uf145\uf146\uf147\uf148\uf149\uf14a\uf14b\uf14c\uf14d\uf14e\uf14f\uf150\uf151\uf152\uf153\uf154\uf155\uf156\uf157\uf158\uf159\uf15a\uf15b\uf15c\uf15d\uf15e\uf15f\uf160\uf161\uf162\uf163\uf164\uf165\uf166\uf167\uf168\uf169\uf16a\uf16b\uf16c\uf16d\uf16e\uf16f\uf170\uf171\uf172\uf173\uf174\uf175\uf176\uf177\uf178\uf179\uf17a\uf17b\uf17c\uf17d\uf17e\uf17f\uf180\uf181\uf182\uf183\uf184\uf185\uf186\uf187\uf188\uf189\uf18a\uf18b\uf18c\uf18d\uf18e\uf18f\uf190\uf191\uf192\uf193\uf194\uf195\uf196\uf197\uf198\uf199\uf19a\uf19b\uf19c\uf19d\uf19e\uf19f\uf1a0\uf1a1\uf1a2\uf1a3\uf1a4\uf1a5\uf1a6\uf1a7\uf1a8\uf1a9\uf1aa\uf1ab\uf1ac\uf1ad\uf1ae\uf1af\uf1b0\uf1b1\uf1b2\uf1b3\uf1b4\uf1b5\uf1b6\uf1b7\uf1b8\uf1b9\uf1ba\uf1bb\uf1bc\uf1bd\uf1be\uf1bf\uf1c0\uf1c1\uf1c2\uf1c3\uf1c4\uf1c5\uf1c6\uf1c7\uf1c8\uf1c9\uf1ca\uf1cb\uf1cc\uf1cd\uf1ce\uf1cf\uf1d0\uf1d1\uf1d2\uf1d3\uf1d4\uf1d5\uf1d6\uf1d7\uf1d8\uf1d9\uf1da\uf1db\uf1dc\uf1dd\uf1de\uf1df\uf1e0\uf1e1\uf1e2\uf1e3\uf1e4\uf1e5\uf1e6\uf1e7\uf1e8\uf1e9\uf1ea\uf1eb\uf1ec\uf1ed\uf1ee\uf1ef\uf1f0\uf1f1\uf1f2\uf1f3\uf1f4\uf1f5\uf1f6\uf1f7\uf1f8\uf1f9\uf1fa\uf1fb\uf1fc\uf1fd\uf1fe\uf1ff\uf200\uf201\uf202\uf203\uf204\uf205\uf206\uf207\uf208\uf209\uf20a\uf20b\uf20c\uf20d\uf20e\uf20f\uf210\uf211\uf212\uf213\uf214\uf215\uf216\uf217\uf218\uf219\uf21a\uf21b\uf21c\uf21d\uf21e\uf21f\uf220\uf221\uf222\uf223\uf224\uf225\uf226\uf227\uf228\uf229\uf22a\uf22b\uf22c\uf22d\uf22e\uf22f\uf230\uf231\uf232\uf233\uf234\uf235\uf236\uf237\uf238\uf239\uf23a\uf23b\uf23c\uf23d\uf23e\uf23f\uf240\uf241\uf242\uf243\uf244\uf245\uf246\uf247\uf248\uf249\uf24a\uf24b\uf24c\uf24d\uf24e\uf24f\uf250\uf251\uf252\uf253\uf254\uf255\uf256\uf257\uf258\uf259\uf25a\uf25b\uf25c\uf25d\uf25e\uf25f\uf260\uf261\uf262\uf263\uf264\uf265\uf266\uf267\uf268\uf269\uf26a\uf26b\uf26c\uf26d\uf26e\uf26f\uf270\uf271\uf272\uf273\uf274\uf275\uf276\uf277\uf278\uf279\uf27a\uf27b\uf27c\uf27d\uf27e\uf27f\uf280\uf281\uf282\uf283\uf284\uf285\uf286\uf287\uf288\uf289\uf28a\uf28b\uf28c\uf28d\uf28e\uf28f\uf290\uf291\uf292\uf293\uf294\uf295\uf296\uf297\uf298\uf299\uf29a\uf29b\uf29c\uf29d\uf29e\uf29f\uf2a0\uf2a1\uf2a2\uf2a3\uf2a4\uf2a5\uf2a6\uf2a7\uf2a8\uf2a9\uf2aa\uf2ab\uf2ac\uf2ad\uf2ae\uf2af\uf2b0\uf2b1\uf2b2\uf2b3\uf2b4\uf2b5\uf2b6\uf2b7\uf2b8\uf2b9\uf2ba\uf2bb\uf2bc\uf2bd\uf2be\uf2bf\uf2c0\uf2c1\uf2c2\uf2c3\uf2c4\uf2c5\uf2c6\uf2c7\uf2c8\uf2c9\uf2ca\uf2cb\uf2cc\uf2cd\uf2ce\uf2cf\uf2d0\uf2d1\uf2d2\uf2d3\uf2d4\uf2d5\uf2d6\uf2d7\uf2d8\uf2d9\uf2da\uf2db\uf2dc\uf2dd\uf2de\uf2df\uf2e0\uf2e1\uf2e2\uf2e3\uf2e4\uf2e5\uf2e6\uf2e7\uf2e8\uf2e9\uf2ea\uf2eb\uf2ec\uf2ed\uf2ee\uf2ef\uf2f0\uf2f1\uf2f2\uf2f3\uf2f4\uf2f5\uf2f6\uf2f7\uf2f8\uf2f9\uf2fa\uf2fb\uf2fc\uf2fd\uf2fe\uf2ff\uf300\uf301\uf302\uf303\uf304\uf305\uf306\uf307\uf308\uf309\uf30a\uf30b\uf30c\uf30d\uf30e\uf30f\uf310\uf311\uf312\uf313\uf314\uf315\uf316\uf317\uf318\uf319\uf31a\uf31b\uf31c\uf31d\uf31e\uf31f\uf320\uf321\uf322\uf323\uf324\uf325\uf326\uf327\uf328\uf329\uf32a\uf32b\uf32c\uf32d\uf32e\uf32f\uf330\uf331\uf332\uf333\uf334\uf335\uf336\uf337\uf338\uf339\uf33a\uf33b\uf33c\uf33d\uf33e\uf33f\uf340\uf341\uf342\uf343\uf344\uf345\uf346\uf347\uf348\uf349\uf34a\uf34b\uf34c\uf34d\uf34e\uf34f\uf350\uf351\uf352\uf353\uf354\uf355\uf356\uf357\uf358\uf359\uf35a\uf35b\uf35c\uf35d\uf35e\uf35f\uf360\uf361\uf362\uf363\uf364\uf365\uf366\uf367\uf368\uf369\uf36a\uf36b\uf36c\uf36d\uf36e\uf36f\uf370\uf371\uf372\uf373\uf374\uf375\uf376\uf377\uf378\uf379\uf37a\uf37b\uf37c\uf37d\uf37e\uf37f\uf380\uf381\uf382\uf383\uf384\uf385\uf386\uf387\uf388\uf389\uf38a\uf38b\uf38c\uf38d\uf38e\uf38f\uf390\uf391\uf392\uf393\uf394\uf395\uf396\uf397\uf398\uf399\uf39a\uf39b\uf39c\uf39d\uf39e\uf39f\uf3a0\uf3a1\uf3a2\uf3a3\uf3a4\uf3a5\uf3a6\uf3a7\uf3a8\uf3a9\uf3aa\uf3ab\uf3ac\uf3ad\uf3ae\uf3af\uf3b0\uf3b1\uf3b2\uf3b3\uf3b4\uf3b5\uf3b6\uf3b7\uf3b8\uf3b9\uf3ba\uf3bb\uf3bc\uf3bd\uf3be\uf3bf\uf3c0\uf3c1\uf3c2\uf3c3\uf3c4\uf3c5\uf3c6\uf3c7\uf3c8\uf3c9\uf3ca\uf3cb\uf3cc\uf3cd\uf3ce\uf3cf\uf3d0\uf3d1\uf3d2\uf3d3\uf3d4\uf3d5\uf3d6\uf3d7\uf3d8\uf3d9\uf3da\uf3db\uf3dc\uf3dd\uf3de\uf3df\uf3e0\uf3e1\uf3e2\uf3e3\uf3e4\uf3e5\uf3e6\uf3e7\uf3e8\uf3e9\uf3ea\uf3eb\uf3ec\uf3ed\uf3ee\uf3ef\uf3f0\uf3f1\uf3f2\uf3f3\uf3f4\uf3f5\uf3f6\uf3f7\uf3f8\uf3f9\uf3fa\uf3fb\uf3fc\uf3fd\uf3fe\uf3ff\uf400\uf401\uf402\uf403\uf404\uf405\uf406\uf407\uf408\uf409\uf40a\uf40b\uf40c\uf40d\uf40e\uf40f\uf410\uf411\uf412\uf413\uf414\uf415\uf416\uf417\uf418\uf419\uf41a\uf41b\uf41c\uf41d\uf41e\uf41f\uf420\uf421\uf422\uf423\uf424\uf425\uf426\uf427\uf428\uf429\uf42a\uf42b\uf42c\uf42d\uf42e\uf42f\uf430\uf431\uf432\uf433\uf434\uf435\uf436\uf437\uf438\uf439\uf43a\uf43b\uf43c\uf43d\uf43e\uf43f\uf440\uf441\uf442\uf443\uf444\uf445\uf446\uf447\uf448\uf449\uf44a\uf44b\uf44c\uf44d\uf44e\uf44f\uf450\uf451\uf452\uf453\uf454\uf455\uf456\uf457\uf458\uf459\uf45a\uf45b\uf45c\uf45d\uf45e\uf45f\uf460\uf461\uf462\uf463\uf464\uf465\uf466\uf467\uf468\uf469\uf46a\uf46b\uf46c\uf46d\uf46e\uf46f\uf470\uf471\uf472\uf473\uf474\uf475\uf476\uf477\uf478\uf479\uf47a\uf47b\uf47c\uf47d\uf47e\uf47f\uf480\uf481\uf482\uf483\uf484\uf485\uf486\uf487\uf488\uf489\uf48a\uf48b\uf48c\uf48d\uf48e\uf48f\uf490\uf491\uf492\uf493\uf494\uf495\uf496\uf497\uf498\uf499\uf49a\uf49b\uf49c\uf49d\uf49e\uf49f\uf4a0\uf4a1\uf4a2\uf4a3\uf4a4\uf4a5\uf4a6\uf4a7\uf4a8\uf4a9\uf4aa\uf4ab\uf4ac\uf4ad\uf4ae\uf4af\uf4b0\uf4b1\uf4b2\uf4b3\uf4b4\uf4b5\uf4b6\uf4b7\uf4b8\uf4b9\uf4ba\uf4bb\uf4bc\uf4bd\uf4be\uf4bf\uf4c0\uf4c1\uf4c2\uf4c3\uf4c4\uf4c5\uf4c6\uf4c7\uf4c8\uf4c9\uf4ca\uf4cb\uf4cc\uf4cd\uf4ce\uf4cf\uf4d0\uf4d1\uf4d2\uf4d3\uf4d4\uf4d5\uf4d6\uf4d7\uf4d8\uf4d9\uf4da\uf4db\uf4dc\uf4dd\uf4de\uf4df\uf4e0\uf4e1\uf4e2\uf4e3\uf4e4\uf4e5\uf4e6\uf4e7\uf4e8\uf4e9\uf4ea\uf4eb\uf4ec\uf4ed\uf4ee\uf4ef\uf4f0\uf4f1\uf4f2\uf4f3\uf4f4\uf4f5\uf4f6\uf4f7\uf4f8\uf4f9\uf4fa\uf4fb\uf4fc\uf4fd\uf4fe\uf4ff\uf500\uf501\uf502\uf503\uf504\uf505\uf506\uf507\uf508\uf509\uf50a\uf50b\uf50c\uf50d\uf50e\uf50f\uf510\uf511\uf512\uf513\uf514\uf515\uf516\uf517\uf518\uf519\uf51a\uf51b\uf51c\uf51d\uf51e\uf51f\uf520\uf521\uf522\uf523\uf524\uf525\uf526\uf527\uf528\uf529\uf52a\uf52b\uf52c\uf52d\uf52e\uf52f\uf530\uf531\uf532\uf533\uf534\uf535\uf536\uf537\uf538\uf539\uf53a\uf53b\uf53c\uf53d\uf53e\uf53f\uf540\uf541\uf542\uf543\uf544\uf545\uf546\uf547\uf548\uf549\uf54a\uf54b\uf54c\uf54d\uf54e\uf54f\uf550\uf551\uf552\uf553\uf554\uf555\uf556\uf557\uf558\uf559\uf55a\uf55b\uf55c\uf55d\uf55e\uf55f\uf560\uf561\uf562\uf563\uf564\uf565\uf566\uf567\uf568\uf569\uf56a\uf56b\uf56c\uf56d\uf56e\uf56f\uf570\uf571\uf572\uf573\uf574\uf575\uf576\uf577\uf578\uf579\uf57a\uf57b\uf57c\uf57d\uf57e\uf57f\uf580\uf581\uf582\uf583\uf584\uf585\uf586\uf587\uf588\uf589\uf58a\uf58b\uf58c\uf58d\uf58e\uf58f\uf590\uf591\uf592\uf593\uf594\uf595\uf596\uf597\uf598\uf599\uf59a\uf59b\uf59c\uf59d\uf59e\uf59f\uf5a0\uf5a1\uf5a2\uf5a3\uf5a4\uf5a5\uf5a6\uf5a7\uf5a8\uf5a9\uf5aa\uf5ab\uf5ac\uf5ad\uf5ae\uf5af\uf5b0\uf5b1\uf5b2\uf5b3\uf5b4\uf5b5\uf5b6\uf5b7\uf5b8\uf5b9\uf5ba\uf5bb\uf5bc\uf5bd\uf5be\uf5bf\uf5c0\uf5c1\uf5c2\uf5c3\uf5c4\uf5c5\uf5c6\uf5c7\uf5c8\uf5c9\uf5ca\uf5cb\uf5cc\uf5cd\uf5ce\uf5cf\uf5d0\uf5d1\uf5d2\uf5d3\uf5d4\uf5d5\uf5d6\uf5d7\uf5d8\uf5d9\uf5da\uf5db\uf5dc\uf5dd\uf5de\uf5df\uf5e0\uf5e1\uf5e2\uf5e3\uf5e4\uf5e5\uf5e6\uf5e7\uf5e8\uf5e9\uf5ea\uf5eb\uf5ec\uf5ed\uf5ee\uf5ef\uf5f0\uf5f1\uf5f2\uf5f3\uf5f4\uf5f5\uf5f6\uf5f7\uf5f8\uf5f9\uf5fa\uf5fb\uf5fc\uf5fd\uf5fe\uf5ff\uf600\uf601\uf602\uf603\uf604\uf605\uf606\uf607\uf608\uf609\uf60a\uf60b\uf60c\uf60d\uf60e\uf60f\uf610\uf611\uf612\uf613\uf614\uf615\uf616\uf617\uf618\uf619\uf61a\uf61b\uf61c\uf61d\uf61e\uf61f\uf620\uf621\uf622\uf623\uf624\uf625\uf626\uf627\uf628\uf629\uf62a\uf62b\uf62c\uf62d\uf62e\uf62f\uf630\uf631\uf632\uf633\uf634\uf635\uf636\uf637\uf638\uf639\uf63a\uf63b\uf63c\uf63d\uf63e\uf63f\uf640\uf641\uf642\uf643\uf644\uf645\uf646\uf647\uf648\uf649\uf64a\uf64b\uf64c\uf64d\uf64e\uf64f\uf650\uf651\uf652\uf653\uf654\uf655\uf656\uf657\uf658\uf659\uf65a\uf65b\uf65c\uf65d\uf65e\uf65f\uf660\uf661\uf662\uf663\uf664\uf665\uf666\uf667\uf668\uf669\uf66a\uf66b\uf66c\uf66d\uf66e\uf66f\uf670\uf671\uf672\uf673\uf674\uf675\uf676\uf677\uf678\uf679\uf67a\uf67b\uf67c\uf67d\uf67e\uf67f\uf680\uf681\uf682\uf683\uf684\uf685\uf686\uf687\uf688\uf689\uf68a\uf68b\uf68c\uf68d\uf68e\uf68f\uf690\uf691\uf692\uf693\uf694\uf695\uf696\uf697\uf698\uf699\uf69a\uf69b\uf69c\uf69d\uf69e\uf69f\uf6a0\uf6a1\uf6a2\uf6a3\uf6a4\uf6a5\uf6a6\uf6a7\uf6a8\uf6a9\uf6aa\uf6ab\uf6ac\uf6ad\uf6ae\uf6af\uf6b0\uf6b1\uf6b2\uf6b3\uf6b4\uf6b5\uf6b6\uf6b7\uf6b8\uf6b9\uf6ba\uf6bb\uf6bc\uf6bd\uf6be\uf6bf\uf6c0\uf6c1\uf6c2\uf6c3\uf6c4\uf6c5\uf6c6\uf6c7\uf6c8\uf6c9\uf6ca\uf6cb\uf6cc\uf6cd\uf6ce\uf6cf\uf6d0\uf6d1\uf6d2\uf6d3\uf6d4\uf6d5\uf6d6\uf6d7\uf6d8\uf6d9\uf6da\uf6db\uf6dc\uf6dd\uf6de\uf6df\uf6e0\uf6e1\uf6e2\uf6e3\uf6e4\uf6e5\uf6e6\uf6e7\uf6e8\uf6e9\uf6ea\uf6eb\uf6ec\uf6ed\uf6ee\uf6ef\uf6f0\uf6f1\uf6f2\uf6f3\uf6f4\uf6f5\uf6f6\uf6f7\uf6f8\uf6f9\uf6fa\uf6fb\uf6fc\uf6fd\uf6fe\uf6ff\uf700\uf701\uf702\uf703\uf704\uf705\uf706\uf707\uf708\uf709\uf70a\uf70b\uf70c\uf70d\uf70e\uf70f\uf710\uf711\uf712\uf713\uf714\uf715\uf716\uf717\uf718\uf719\uf71a\uf71b\uf71c\uf71d\uf71e\uf71f\uf720\uf721\uf722\uf723\uf724\uf725\uf726\uf727\uf728\uf729\uf72a\uf72b\uf72c\uf72d\uf72e\uf72f\uf730\uf731\uf732\uf733\uf734\uf735\uf736\uf737\uf738\uf739\uf73a\uf73b\uf73c\uf73d\uf73e\uf73f\uf740\uf741\uf742\uf743\uf744\uf745\uf746\uf747\uf748\uf749\uf74a\uf74b\uf74c\uf74d\uf74e\uf74f\uf750\uf751\uf752\uf753\uf754\uf755\uf756\uf757\uf758\uf759\uf75a\uf75b\uf75c\uf75d\uf75e\uf75f\uf760\uf761\uf762\uf763\uf764\uf765\uf766\uf767\uf768\uf769\uf76a\uf76b\uf76c\uf76d\uf76e\uf76f\uf770\uf771\uf772\uf773\uf774\uf775\uf776\uf777\uf778\uf779\uf77a\uf77b\uf77c\uf77d\uf77e\uf77f\uf780\uf781\uf782\uf783\uf784\uf785\uf786\uf787\uf788\uf789\uf78a\uf78b\uf78c\uf78d\uf78e\uf78f\uf790\uf791\uf792\uf793\uf794\uf795\uf796\uf797\uf798\uf799\uf79a\uf79b\uf79c\uf79d\uf79e\uf79f\uf7a0\uf7a1\uf7a2\uf7a3\uf7a4\uf7a5\uf7a6\uf7a7\uf7a8\uf7a9\uf7aa\uf7ab\uf7ac\uf7ad\uf7ae\uf7af\uf7b0\uf7b1\uf7b2\uf7b3\uf7b4\uf7b5\uf7b6\uf7b7\uf7b8\uf7b9\uf7ba\uf7bb\uf7bc\uf7bd\uf7be\uf7bf\uf7c0\uf7c1\uf7c2\uf7c3\uf7c4\uf7c5\uf7c6\uf7c7\uf7c8\uf7c9\uf7ca\uf7cb\uf7cc\uf7cd\uf7ce\uf7cf\uf7d0\uf7d1\uf7d2\uf7d3\uf7d4\uf7d5\uf7d6\uf7d7\uf7d8\uf7d9\uf7da\uf7db\uf7dc\uf7dd\uf7de\uf7df\uf7e0\uf7e1\uf7e2\uf7e3\uf7e4\uf7e5\uf7e6\uf7e7\uf7e8\uf7e9\uf7ea\uf7eb\uf7ec\uf7ed\uf7ee\uf7ef\uf7f0\uf7f1\uf7f2\uf7f3\uf7f4\uf7f5\uf7f6\uf7f7\uf7f8\uf7f9\uf7fa\uf7fb\uf7fc\uf7fd\uf7fe\uf7ff\uf800\uf801\uf802\uf803\uf804\uf805\uf806\uf807\uf808\uf809\uf80a\uf80b\uf80c\uf80d\uf80e\uf80f\uf810\uf811\uf812\uf813\uf814\uf815\uf816\uf817\uf818\uf819\uf81a\uf81b\uf81c\uf81d\uf81e\uf81f\uf820\uf821\uf822\uf823\uf824\uf825\uf826\uf827\uf828\uf829\uf82a\uf82b\uf82c\uf82d\uf82e\uf82f\uf830\uf831\uf832\uf833\uf834\uf835\uf836\uf837\uf838\uf839\uf83a\uf83b\uf83c\uf83d\uf83e\uf83f\uf840\uf841\uf842\uf843\uf844\uf845\uf846\uf847\uf848\uf849\uf84a\uf84b\uf84c\uf84d\uf84e\uf84f\uf850\uf851\uf852\uf853\uf854\uf855\uf856\uf857\uf858\uf859\uf85a\uf85b\uf85c\uf85d\uf85e\uf85f\uf860\uf861\uf862\uf863\uf864\uf865\uf866\uf867\uf868\uf869\uf86a\uf86b\uf86c\uf86d\uf86e\uf86f\uf870\uf871\uf872\uf873\uf874\uf875\uf876\uf877\uf878\uf879\uf87a\uf87b\uf87c\uf87d\uf87e\uf87f\uf880\uf881\uf882\uf883\uf884\uf885\uf886\uf887\uf888\uf889\uf88a\uf88b\uf88c\uf88d\uf88e\uf88f\uf890\uf891\uf892\uf893\uf894\uf895\uf896\uf897\uf898\uf899\uf89a\uf89b\uf89c\uf89d\uf89e\uf89f\uf8a0\uf8a1\uf8a2\uf8a3\uf8a4\uf8a5\uf8a6\uf8a7\uf8a8\uf8a9\uf8aa\uf8ab\uf8ac\uf8ad\uf8ae\uf8af\uf8b0\uf8b1\uf8b2\uf8b3\uf8b4\uf8b5\uf8b6\uf8b7\uf8b8\uf8b9\uf8ba\uf8bb\uf8bc\uf8bd\uf8be\uf8bf\uf8c0\uf8c1\uf8c2\uf8c3\uf8c4\uf8c5\uf8c6\uf8c7\uf8c8\uf8c9\uf8ca\uf8cb\uf8cc\uf8cd\uf8ce\uf8cf\uf8d0\uf8d1\uf8d2\uf8d3\uf8d4\uf8d5\uf8d6\uf8d7\uf8d8\uf8d9\uf8da\uf8db\uf8dc\uf8dd\uf8de\uf8df\uf8e0\uf8e1\uf8e2\uf8e3\uf8e4\uf8e5\uf8e6\uf8e7\uf8e8\uf8e9\uf8ea\uf8eb\uf8ec\uf8ed\uf8ee\uf8ef\uf8f0\uf8f1\uf8f2\uf8f3\uf8f4\uf8f5\uf8f6\uf8f7\uf8f8\uf8f9\uf8fa\uf8fb\uf8fc\uf8fd\uf8fe\uf8ff'
+
+try:
+ Cs = eval(r"'\ud800\ud801\ud802\ud803\ud804\ud805\ud806\ud807\ud808\ud809\ud80a\ud80b\ud80c\ud80d\ud80e\ud80f\ud810\ud811\ud812\ud813\ud814\ud815\ud816\ud817\ud818\ud819\ud81a\ud81b\ud81c\ud81d\ud81e\ud81f\ud820\ud821\ud822\ud823\ud824\ud825\ud826\ud827\ud828\ud829\ud82a\ud82b\ud82c\ud82d\ud82e\ud82f\ud830\ud831\ud832\ud833\ud834\ud835\ud836\ud837\ud838\ud839\ud83a\ud83b\ud83c\ud83d\ud83e\ud83f\ud840\ud841\ud842\ud843\ud844\ud845\ud846\ud847\ud848\ud849\ud84a\ud84b\ud84c\ud84d\ud84e\ud84f\ud850\ud851\ud852\ud853\ud854\ud855\ud856\ud857\ud858\ud859\ud85a\ud85b\ud85c\ud85d\ud85e\ud85f\ud860\ud861\ud862\ud863\ud864\ud865\ud866\ud867\ud868\ud869\ud86a\ud86b\ud86c\ud86d\ud86e\ud86f\ud870\ud871\ud872\ud873\ud874\ud875\ud876\ud877\ud878\ud879\ud87a\ud87b\ud87c\ud87d\ud87e\ud87f\ud880\ud881\ud882\ud883\ud884\ud885\ud886\ud887\ud888\ud889\ud88a\ud88b\ud88c\ud88d\ud88e\ud88f\ud890\ud891\ud892\ud893\ud894\ud895\ud896\ud897\ud898\ud899\ud89a\ud89b\ud89c\ud89d\ud89e\ud89f\ud8a0\ud8a1\ud8a2\ud8a3\ud8a4\ud8a5\ud8a6\ud8a7\ud8a8\ud8a9\ud8aa\ud8ab\ud8ac\ud8ad\ud8ae\ud8af\ud8b0\ud8b1\ud8b2\ud8b3\ud8b4\ud8b5\ud8b6\ud8b7\ud8b8\ud8b9\ud8ba\ud8bb\ud8bc\ud8bd\ud8be\ud8bf\ud8c0\ud8c1\ud8c2\ud8c3\ud8c4\ud8c5\ud8c6\ud8c7\ud8c8\ud8c9\ud8ca\ud8cb\ud8cc\ud8cd\ud8ce\ud8cf\ud8d0\ud8d1\ud8d2\ud8d3\ud8d4\ud8d5\ud8d6\ud8d7\ud8d8\ud8d9\ud8da\ud8db\ud8dc\ud8dd\ud8de\ud8df\ud8e0\ud8e1\ud8e2\ud8e3\ud8e4\ud8e5\ud8e6\ud8e7\ud8e8\ud8e9\ud8ea\ud8eb\ud8ec\ud8ed\ud8ee\ud8ef\ud8f0\ud8f1\ud8f2\ud8f3\ud8f4\ud8f5\ud8f6\ud8f7\ud8f8\ud8f9\ud8fa\ud8fb\ud8fc\ud8fd\ud8fe\ud8ff\ud900\ud901\ud902\ud903\ud904\ud905\ud906\ud907\ud908\ud909\ud90a\ud90b\ud90c\ud90d\ud90e\ud90f\ud910\ud911\ud912\ud913\ud914\ud915\ud916\ud917\ud918\ud919\ud91a\ud91b\ud91c\ud91d\ud91e\ud91f\ud920\ud921\ud922\ud923\ud924\ud925\ud926\ud927\ud928\ud929\ud92a\ud92b\ud92c\ud92d\ud92e\ud92f\ud930\ud931\ud932\ud933\ud934\ud935\ud936\ud937\ud938\ud939\ud93a\ud93b\ud93c\ud93d\ud93e\ud93f\ud940\ud941\ud942\ud943\ud944\ud945\ud946\ud947\ud948\ud949\ud94a\ud94b\ud94c\ud94d\ud94e\ud94f\ud950\ud951\ud952\ud953\ud954\ud955\ud956\ud957\ud958\ud959\ud95a\ud95b\ud95c\ud95d\ud95e\ud95f\ud960\ud961\ud962\ud963\ud964\ud965\ud966\ud967\ud968\ud969\ud96a\ud96b\ud96c\ud96d\ud96e\ud96f\ud970\ud971\ud972\ud973\ud974\ud975\ud976\ud977\ud978\ud979\ud97a\ud97b\ud97c\ud97d\ud97e\ud97f\ud980\ud981\ud982\ud983\ud984\ud985\ud986\ud987\ud988\ud989\ud98a\ud98b\ud98c\ud98d\ud98e\ud98f\ud990\ud991\ud992\ud993\ud994\ud995\ud996\ud997\ud998\ud999\ud99a\ud99b\ud99c\ud99d\ud99e\ud99f\ud9a0\ud9a1\ud9a2\ud9a3\ud9a4\ud9a5\ud9a6\ud9a7\ud9a8\ud9a9\ud9aa\ud9ab\ud9ac\ud9ad\ud9ae\ud9af\ud9b0\ud9b1\ud9b2\ud9b3\ud9b4\ud9b5\ud9b6\ud9b7\ud9b8\ud9b9\ud9ba\ud9bb\ud9bc\ud9bd\ud9be\ud9bf\ud9c0\ud9c1\ud9c2\ud9c3\ud9c4\ud9c5\ud9c6\ud9c7\ud9c8\ud9c9\ud9ca\ud9cb\ud9cc\ud9cd\ud9ce\ud9cf\ud9d0\ud9d1\ud9d2\ud9d3\ud9d4\ud9d5\ud9d6\ud9d7\ud9d8\ud9d9\ud9da\ud9db\ud9dc\ud9dd\ud9de\ud9df\ud9e0\ud9e1\ud9e2\ud9e3\ud9e4\ud9e5\ud9e6\ud9e7\ud9e8\ud9e9\ud9ea\ud9eb\ud9ec\ud9ed\ud9ee\ud9ef\ud9f0\ud9f1\ud9f2\ud9f3\ud9f4\ud9f5\ud9f6\ud9f7\ud9f8\ud9f9\ud9fa\ud9fb\ud9fc\ud9fd\ud9fe\ud9ff\uda00\uda01\uda02\uda03\uda04\uda05\uda06\uda07\uda08\uda09\uda0a\uda0b\uda0c\uda0d\uda0e\uda0f\uda10\uda11\uda12\uda13\uda14\uda15\uda16\uda17\uda18\uda19\uda1a\uda1b\uda1c\uda1d\uda1e\uda1f\uda20\uda21\uda22\uda23\uda24\uda25\uda26\uda27\uda28\uda29\uda2a\uda2b\uda2c\uda2d\uda2e\uda2f\uda30\uda31\uda32\uda33\uda34\uda35\uda36\uda37\uda38\uda39\uda3a\uda3b\uda3c\uda3d\uda3e\uda3f\uda40\uda41\uda42\uda43\uda44\uda45\uda46\uda47\uda48\uda49\uda4a\uda4b\uda4c\uda4d\uda4e\uda4f\uda50\uda51\uda52\uda53\uda54\uda55\uda56\uda57\uda58\uda59\uda5a\uda5b\uda5c\uda5d\uda5e\uda5f\uda60\uda61\uda62\uda63\uda64\uda65\uda66\uda67\uda68\uda69\uda6a\uda6b\uda6c\uda6d\uda6e\uda6f\uda70\uda71\uda72\uda73\uda74\uda75\uda76\uda77\uda78\uda79\uda7a\uda7b\uda7c\uda7d\uda7e\uda7f\uda80\uda81\uda82\uda83\uda84\uda85\uda86\uda87\uda88\uda89\uda8a\uda8b\uda8c\uda8d\uda8e\uda8f\uda90\uda91\uda92\uda93\uda94\uda95\uda96\uda97\uda98\uda99\uda9a\uda9b\uda9c\uda9d\uda9e\uda9f\udaa0\udaa1\udaa2\udaa3\udaa4\udaa5\udaa6\udaa7\udaa8\udaa9\udaaa\udaab\udaac\udaad\udaae\udaaf\udab0\udab1\udab2\udab3\udab4\udab5\udab6\udab7\udab8\udab9\udaba\udabb\udabc\udabd\udabe\udabf\udac0\udac1\udac2\udac3\udac4\udac5\udac6\udac7\udac8\udac9\udaca\udacb\udacc\udacd\udace\udacf\udad0\udad1\udad2\udad3\udad4\udad5\udad6\udad7\udad8\udad9\udada\udadb\udadc\udadd\udade\udadf\udae0\udae1\udae2\udae3\udae4\udae5\udae6\udae7\udae8\udae9\udaea\udaeb\udaec\udaed\udaee\udaef\udaf0\udaf1\udaf2\udaf3\udaf4\udaf5\udaf6\udaf7\udaf8\udaf9\udafa\udafb\udafc\udafd\udafe\udaff\udb00\udb01\udb02\udb03\udb04\udb05\udb06\udb07\udb08\udb09\udb0a\udb0b\udb0c\udb0d\udb0e\udb0f\udb10\udb11\udb12\udb13\udb14\udb15\udb16\udb17\udb18\udb19\udb1a\udb1b\udb1c\udb1d\udb1e\udb1f\udb20\udb21\udb22\udb23\udb24\udb25\udb26\udb27\udb28\udb29\udb2a\udb2b\udb2c\udb2d\udb2e\udb2f\udb30\udb31\udb32\udb33\udb34\udb35\udb36\udb37\udb38\udb39\udb3a\udb3b\udb3c\udb3d\udb3e\udb3f\udb40\udb41\udb42\udb43\udb44\udb45\udb46\udb47\udb48\udb49\udb4a\udb4b\udb4c\udb4d\udb4e\udb4f\udb50\udb51\udb52\udb53\udb54\udb55\udb56\udb57\udb58\udb59\udb5a\udb5b\udb5c\udb5d\udb5e\udb5f\udb60\udb61\udb62\udb63\udb64\udb65\udb66\udb67\udb68\udb69\udb6a\udb6b\udb6c\udb6d\udb6e\udb6f\udb70\udb71\udb72\udb73\udb74\udb75\udb76\udb77\udb78\udb79\udb7a\udb7b\udb7c\udb7d\udb7e\udb7f\udb80\udb81\udb82\udb83\udb84\udb85\udb86\udb87\udb88\udb89\udb8a\udb8b\udb8c\udb8d\udb8e\udb8f\udb90\udb91\udb92\udb93\udb94\udb95\udb96\udb97\udb98\udb99\udb9a\udb9b\udb9c\udb9d\udb9e\udb9f\udba0\udba1\udba2\udba3\udba4\udba5\udba6\udba7\udba8\udba9\udbaa\udbab\udbac\udbad\udbae\udbaf\udbb0\udbb1\udbb2\udbb3\udbb4\udbb5\udbb6\udbb7\udbb8\udbb9\udbba\udbbb\udbbc\udbbd\udbbe\udbbf\udbc0\udbc1\udbc2\udbc3\udbc4\udbc5\udbc6\udbc7\udbc8\udbc9\udbca\udbcb\udbcc\udbcd\udbce\udbcf\udbd0\udbd1\udbd2\udbd3\udbd4\udbd5\udbd6\udbd7\udbd8\udbd9\udbda\udbdb\udbdc\udbdd\udbde\udbdf\udbe0\udbe1\udbe2\udbe3\udbe4\udbe5\udbe6\udbe7\udbe8\udbe9\udbea\udbeb\udbec\udbed\udbee\udbef\udbf0\udbf1\udbf2\udbf3\udbf4\udbf5\udbf6\udbf7\udbf8\udbf9\udbfa\udbfb\udbfc\udbfd\udbfe\U0010fc00\udc01\udc02\udc03\udc04\udc05\udc06\udc07\udc08\udc09\udc0a\udc0b\udc0c\udc0d\udc0e\udc0f\udc10\udc11\udc12\udc13\udc14\udc15\udc16\udc17\udc18\udc19\udc1a\udc1b\udc1c\udc1d\udc1e\udc1f\udc20\udc21\udc22\udc23\udc24\udc25\udc26\udc27\udc28\udc29\udc2a\udc2b\udc2c\udc2d\udc2e\udc2f\udc30\udc31\udc32\udc33\udc34\udc35\udc36\udc37\udc38\udc39\udc3a\udc3b\udc3c\udc3d\udc3e\udc3f\udc40\udc41\udc42\udc43\udc44\udc45\udc46\udc47\udc48\udc49\udc4a\udc4b\udc4c\udc4d\udc4e\udc4f\udc50\udc51\udc52\udc53\udc54\udc55\udc56\udc57\udc58\udc59\udc5a\udc5b\udc5c\udc5d\udc5e\udc5f\udc60\udc61\udc62\udc63\udc64\udc65\udc66\udc67\udc68\udc69\udc6a\udc6b\udc6c\udc6d\udc6e\udc6f\udc70\udc71\udc72\udc73\udc74\udc75\udc76\udc77\udc78\udc79\udc7a\udc7b\udc7c\udc7d\udc7e\udc7f\udc80\udc81\udc82\udc83\udc84\udc85\udc86\udc87\udc88\udc89\udc8a\udc8b\udc8c\udc8d\udc8e\udc8f\udc90\udc91\udc92\udc93\udc94\udc95\udc96\udc97\udc98\udc99\udc9a\udc9b\udc9c\udc9d\udc9e\udc9f\udca0\udca1\udca2\udca3\udca4\udca5\udca6\udca7\udca8\udca9\udcaa\udcab\udcac\udcad\udcae\udcaf\udcb0\udcb1\udcb2\udcb3\udcb4\udcb5\udcb6\udcb7\udcb8\udcb9\udcba\udcbb\udcbc\udcbd\udcbe\udcbf\udcc0\udcc1\udcc2\udcc3\udcc4\udcc5\udcc6\udcc7\udcc8\udcc9\udcca\udccb\udccc\udccd\udcce\udccf\udcd0\udcd1\udcd2\udcd3\udcd4\udcd5\udcd6\udcd7\udcd8\udcd9\udcda\udcdb\udcdc\udcdd\udcde\udcdf\udce0\udce1\udce2\udce3\udce4\udce5\udce6\udce7\udce8\udce9\udcea\udceb\udcec\udced\udcee\udcef\udcf0\udcf1\udcf2\udcf3\udcf4\udcf5\udcf6\udcf7\udcf8\udcf9\udcfa\udcfb\udcfc\udcfd\udcfe\udcff\udd00\udd01\udd02\udd03\udd04\udd05\udd06\udd07\udd08\udd09\udd0a\udd0b\udd0c\udd0d\udd0e\udd0f\udd10\udd11\udd12\udd13\udd14\udd15\udd16\udd17\udd18\udd19\udd1a\udd1b\udd1c\udd1d\udd1e\udd1f\udd20\udd21\udd22\udd23\udd24\udd25\udd26\udd27\udd28\udd29\udd2a\udd2b\udd2c\udd2d\udd2e\udd2f\udd30\udd31\udd32\udd33\udd34\udd35\udd36\udd37\udd38\udd39\udd3a\udd3b\udd3c\udd3d\udd3e\udd3f\udd40\udd41\udd42\udd43\udd44\udd45\udd46\udd47\udd48\udd49\udd4a\udd4b\udd4c\udd4d\udd4e\udd4f\udd50\udd51\udd52\udd53\udd54\udd55\udd56\udd57\udd58\udd59\udd5a\udd5b\udd5c\udd5d\udd5e\udd5f\udd60\udd61\udd62\udd63\udd64\udd65\udd66\udd67\udd68\udd69\udd6a\udd6b\udd6c\udd6d\udd6e\udd6f\udd70\udd71\udd72\udd73\udd74\udd75\udd76\udd77\udd78\udd79\udd7a\udd7b\udd7c\udd7d\udd7e\udd7f\udd80\udd81\udd82\udd83\udd84\udd85\udd86\udd87\udd88\udd89\udd8a\udd8b\udd8c\udd8d\udd8e\udd8f\udd90\udd91\udd92\udd93\udd94\udd95\udd96\udd97\udd98\udd99\udd9a\udd9b\udd9c\udd9d\udd9e\udd9f\udda0\udda1\udda2\udda3\udda4\udda5\udda6\udda7\udda8\udda9\uddaa\uddab\uddac\uddad\uddae\uddaf\uddb0\uddb1\uddb2\uddb3\uddb4\uddb5\uddb6\uddb7\uddb8\uddb9\uddba\uddbb\uddbc\uddbd\uddbe\uddbf\uddc0\uddc1\uddc2\uddc3\uddc4\uddc5\uddc6\uddc7\uddc8\uddc9\uddca\uddcb\uddcc\uddcd\uddce\uddcf\uddd0\uddd1\uddd2\uddd3\uddd4\uddd5\uddd6\uddd7\uddd8\uddd9\uddda\udddb\udddc\udddd\uddde\udddf\udde0\udde1\udde2\udde3\udde4\udde5\udde6\udde7\udde8\udde9\uddea\uddeb\uddec\udded\uddee\uddef\uddf0\uddf1\uddf2\uddf3\uddf4\uddf5\uddf6\uddf7\uddf8\uddf9\uddfa\uddfb\uddfc\uddfd\uddfe\uddff\ude00\ude01\ude02\ude03\ude04\ude05\ude06\ude07\ude08\ude09\ude0a\ude0b\ude0c\ude0d\ude0e\ude0f\ude10\ude11\ude12\ude13\ude14\ude15\ude16\ude17\ude18\ude19\ude1a\ude1b\ude1c\ude1d\ude1e\ude1f\ude20\ude21\ude22\ude23\ude24\ude25\ude26\ude27\ude28\ude29\ude2a\ude2b\ude2c\ude2d\ude2e\ude2f\ude30\ude31\ude32\ude33\ude34\ude35\ude36\ude37\ude38\ude39\ude3a\ude3b\ude3c\ude3d\ude3e\ude3f\ude40\ude41\ude42\ude43\ude44\ude45\ude46\ude47\ude48\ude49\ude4a\ude4b\ude4c\ude4d\ude4e\ude4f\ude50\ude51\ude52\ude53\ude54\ude55\ude56\ude57\ude58\ude59\ude5a\ude5b\ude5c\ude5d\ude5e\ude5f\ude60\ude61\ude62\ude63\ude64\ude65\ude66\ude67\ude68\ude69\ude6a\ude6b\ude6c\ude6d\ude6e\ude6f\ude70\ude71\ude72\ude73\ude74\ude75\ude76\ude77\ude78\ude79\ude7a\ude7b\ude7c\ude7d\ude7e\ude7f\ude80\ude81\ude82\ude83\ude84\ude85\ude86\ude87\ude88\ude89\ude8a\ude8b\ude8c\ude8d\ude8e\ude8f\ude90\ude91\ude92\ude93\ude94\ude95\ude96\ude97\ude98\ude99\ude9a\ude9b\ude9c\ude9d\ude9e\ude9f\udea0\udea1\udea2\udea3\udea4\udea5\udea6\udea7\udea8\udea9\udeaa\udeab\udeac\udead\udeae\udeaf\udeb0\udeb1\udeb2\udeb3\udeb4\udeb5\udeb6\udeb7\udeb8\udeb9\udeba\udebb\udebc\udebd\udebe\udebf\udec0\udec1\udec2\udec3\udec4\udec5\udec6\udec7\udec8\udec9\udeca\udecb\udecc\udecd\udece\udecf\uded0\uded1\uded2\uded3\uded4\uded5\uded6\uded7\uded8\uded9\udeda\udedb\udedc\udedd\udede\udedf\udee0\udee1\udee2\udee3\udee4\udee5\udee6\udee7\udee8\udee9\udeea\udeeb\udeec\udeed\udeee\udeef\udef0\udef1\udef2\udef3\udef4\udef5\udef6\udef7\udef8\udef9\udefa\udefb\udefc\udefd\udefe\udeff\udf00\udf01\udf02\udf03\udf04\udf05\udf06\udf07\udf08\udf09\udf0a\udf0b\udf0c\udf0d\udf0e\udf0f\udf10\udf11\udf12\udf13\udf14\udf15\udf16\udf17\udf18\udf19\udf1a\udf1b\udf1c\udf1d\udf1e\udf1f\udf20\udf21\udf22\udf23\udf24\udf25\udf26\udf27\udf28\udf29\udf2a\udf2b\udf2c\udf2d\udf2e\udf2f\udf30\udf31\udf32\udf33\udf34\udf35\udf36\udf37\udf38\udf39\udf3a\udf3b\udf3c\udf3d\udf3e\udf3f\udf40\udf41\udf42\udf43\udf44\udf45\udf46\udf47\udf48\udf49\udf4a\udf4b\udf4c\udf4d\udf4e\udf4f\udf50\udf51\udf52\udf53\udf54\udf55\udf56\udf57\udf58\udf59\udf5a\udf5b\udf5c\udf5d\udf5e\udf5f\udf60\udf61\udf62\udf63\udf64\udf65\udf66\udf67\udf68\udf69\udf6a\udf6b\udf6c\udf6d\udf6e\udf6f\udf70\udf71\udf72\udf73\udf74\udf75\udf76\udf77\udf78\udf79\udf7a\udf7b\udf7c\udf7d\udf7e\udf7f\udf80\udf81\udf82\udf83\udf84\udf85\udf86\udf87\udf88\udf89\udf8a\udf8b\udf8c\udf8d\udf8e\udf8f\udf90\udf91\udf92\udf93\udf94\udf95\udf96\udf97\udf98\udf99\udf9a\udf9b\udf9c\udf9d\udf9e\udf9f\udfa0\udfa1\udfa2\udfa3\udfa4\udfa5\udfa6\udfa7\udfa8\udfa9\udfaa\udfab\udfac\udfad\udfae\udfaf\udfb0\udfb1\udfb2\udfb3\udfb4\udfb5\udfb6\udfb7\udfb8\udfb9\udfba\udfbb\udfbc\udfbd\udfbe\udfbf\udfc0\udfc1\udfc2\udfc3\udfc4\udfc5\udfc6\udfc7\udfc8\udfc9\udfca\udfcb\udfcc\udfcd\udfce\udfcf\udfd0\udfd1\udfd2\udfd3\udfd4\udfd5\udfd6\udfd7\udfd8\udfd9\udfda\udfdb\udfdc\udfdd\udfde\udfdf\udfe0\udfe1\udfe2\udfe3\udfe4\udfe5\udfe6\udfe7\udfe8\udfe9\udfea\udfeb\udfec\udfed\udfee\udfef\udff0\udff1\udff2\udff3\udff4\udff5\udff6\udff7\udff8\udff9\udffa\udffb\udffc\udffd\udffe\udfff'")
+except UnicodeDecodeError:
+ Cs = '' # Jython can't handle isolated surrogates
+
+Ll = u'abcdefghijklmnopqrstuvwxyz\xaa\xb5\xba\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e\u017f\u0180\u0183\u0185\u0188\u018c\u018d\u0192\u0195\u0199\u019a\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9\u01ba\u01bd\u01be\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233\u0234\u0235\u0236\u0237\u0238\u0239\u023c\u023f\u0240\u0250\u0251\u0252\u0253\u0254\u0255\u0256\u0257\u0258\u0259\u025a\u025b\u025c\u025d\u025e\u025f\u0260\u0261\u0262\u0263\u0264\u0265\u0266\u0267\u0268\u0269\u026a\u026b\u026c\u026d\u026e\u026f\u0270\u0271\u0272\u0273\u0274\u0275\u0276\u0277\u0278\u0279\u027a\u027b\u027c\u027d\u027e\u027f\u0280\u0281\u0282\u0283\u0284\u0285\u0286\u0287\u0288\u0289\u028a\u028b\u028c\u028d\u028e\u028f\u0290\u0291\u0292\u0293\u0294\u0295\u0296\u0297\u0298\u0299\u029a\u029b\u029c\u029d\u029e\u029f\u02a0\u02a1\u02a2\u02a3\u02a4\u02a5\u02a6\u02a7\u02a8\u02a9\u02aa\u02ab\u02ac\u02ad\u02ae\u02af\u0390\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\u03d0\u03d1\u03d5\u03d6\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef\u03f0\u03f1\u03f2\u03f3\u03f5\u03f8\u03fb\u03fc\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0450\u0451\u0452\u0453\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u045d\u045e\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u1d00\u1d01\u1d02\u1d03\u1d04\u1d05\u1d06\u1d07\u1d08\u1d09\u1d0a\u1d0b\u1d0c\u1d0d\u1d0e\u1d0f\u1d10\u1d11\u1d12\u1d13\u1d14\u1d15\u1d16\u1d17\u1d18\u1d19\u1d1a\u1d1b\u1d1c\u1d1d\u1d1e\u1d1f\u1d20\u1d21\u1d22\u1d23\u1d24\u1d25\u1d26\u1d27\u1d28\u1d29\u1d2a\u1d2b\u1d62\u1d63\u1d64\u1d65\u1d66\u1d67\u1d68\u1d69\u1d6a\u1d6b\u1d6c\u1d6d\u1d6e\u1d6f\u1d70\u1d71\u1d72\u1d73\u1d74\u1d75\u1d76\u1d77\u1d79\u1d7a\u1d7b\u1d7c\u1d7d\u1d7e\u1d7f\u1d80\u1d81\u1d82\u1d83\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a\u1d8b\u1d8c\u1d8d\u1d8e\u1d8f\u1d90\u1d91\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95\u1e96\u1e97\u1e98\u1e99\u1e9a\u1e9b\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1f00\u1f01\u1f02\u1f03\u1f04\u1f05\u1f06\u1f07\u1f10\u1f11\u1f12\u1f13\u1f14\u1f15\u1f20\u1f21\u1f22\u1f23\u1f24\u1f25\u1f26\u1f27\u1f30\u1f31\u1f32\u1f33\u1f34\u1f35\u1f36\u1f37\u1f40\u1f41\u1f42\u1f43\u1f44\u1f45\u1f50\u1f51\u1f52\u1f53\u1f54\u1f55\u1f56\u1f57\u1f60\u1f61\u1f62\u1f63\u1f64\u1f65\u1f66\u1f67\u1f70\u1f71\u1f72\u1f73\u1f74\u1f75\u1f76\u1f77\u1f78\u1f79\u1f7a\u1f7b\u1f7c\u1f7d\u1f80\u1f81\u1f82\u1f83\u1f84\u1f85\u1f86\u1f87\u1f90\u1f91\u1f92\u1f93\u1f94\u1f95\u1f96\u1f97\u1fa0\u1fa1\u1fa2\u1fa3\u1fa4\u1fa5\u1fa6\u1fa7\u1fb0\u1fb1\u1fb2\u1fb3\u1fb4\u1fb6\u1fb7\u1fbe\u1fc2\u1fc3\u1fc4\u1fc6\u1fc7\u1fd0\u1fd1\u1fd2\u1fd3\u1fd6\u1fd7\u1fe0\u1fe1\u1fe2\u1fe3\u1fe4\u1fe5\u1fe6\u1fe7\u1ff2\u1ff3\u1ff4\u1ff6\u1ff7\u2071\u207f\u210a\u210e\u210f\u2113\u212f\u2134\u2139\u213c\u213d\u2146\u2147\u2148\u2149\u2c30\u2c31\u2c32\u2c33\u2c34\u2c35\u2c36\u2c37\u2c38\u2c39\u2c3a\u2c3b\u2c3c\u2c3d\u2c3e\u2c3f\u2c40\u2c41\u2c42\u2c43\u2c44\u2c45\u2c46\u2c47\u2c48\u2c49\u2c4a\u2c4b\u2c4c\u2c4d\u2c4e\u2c4f\u2c50\u2c51\u2c52\u2c53\u2c54\u2c55\u2c56\u2c57\u2c58\u2c59\u2c5a\u2c5b\u2c5c\u2c5d\u2c5e\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3\u2ce4\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d07\u2d08\u2d09\u2d0a\u2d0b\u2d0c\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d13\u2d14\u2d15\u2d16\u2d17\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d1f\u2d20\u2d21\u2d22\u2d23\u2d24\u2d25\ufb00\ufb01\ufb02\ufb03\ufb04\ufb05\ufb06\ufb13\ufb14\ufb15\ufb16\ufb17\uff41\uff42\uff43\uff44\uff45\uff46\uff47\uff48\uff49\uff4a\uff4b\uff4c\uff4d\uff4e\uff4f\uff50\uff51\uff52\uff53\uff54\uff55\uff56\uff57\uff58\uff59\uff5a'
+
+Lm = u'\u02b0\u02b1\u02b2\u02b3\u02b4\u02b5\u02b6\u02b7\u02b8\u02b9\u02ba\u02bb\u02bc\u02bd\u02be\u02bf\u02c0\u02c1\u02c6\u02c7\u02c8\u02c9\u02ca\u02cb\u02cc\u02cd\u02ce\u02cf\u02d0\u02d1\u02e0\u02e1\u02e2\u02e3\u02e4\u02ee\u037a\u0559\u0640\u06e5\u06e6\u0e46\u0ec6\u10fc\u17d7\u1843\u1d2c\u1d2d\u1d2e\u1d2f\u1d30\u1d31\u1d32\u1d33\u1d34\u1d35\u1d36\u1d37\u1d38\u1d39\u1d3a\u1d3b\u1d3c\u1d3d\u1d3e\u1d3f\u1d40\u1d41\u1d42\u1d43\u1d44\u1d45\u1d46\u1d47\u1d48\u1d49\u1d4a\u1d4b\u1d4c\u1d4d\u1d4e\u1d4f\u1d50\u1d51\u1d52\u1d53\u1d54\u1d55\u1d56\u1d57\u1d58\u1d59\u1d5a\u1d5b\u1d5c\u1d5d\u1d5e\u1d5f\u1d60\u1d61\u1d78\u1d9b\u1d9c\u1d9d\u1d9e\u1d9f\u1da0\u1da1\u1da2\u1da3\u1da4\u1da5\u1da6\u1da7\u1da8\u1da9\u1daa\u1dab\u1dac\u1dad\u1dae\u1daf\u1db0\u1db1\u1db2\u1db3\u1db4\u1db5\u1db6\u1db7\u1db8\u1db9\u1dba\u1dbb\u1dbc\u1dbd\u1dbe\u1dbf\u2090\u2091\u2092\u2093\u2094\u2d6f\u3005\u3031\u3032\u3033\u3034\u3035\u303b\u309d\u309e\u30fc\u30fd\u30fe\ua015\uff70\uff9e\uff9f'
+
+Lo = u'\u01bb\u01c0\u01c1\u01c2\u01c3\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u05f0\u05f1\u05f2\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u066e\u066f\u0671\u0672\u0673\u0674\u0675\u0676\u0677\u0678\u0679\u067a\u067b\u067c\u067d\u067e\u067f\u0680\u0681\u0682\u0683\u0684\u0685\u0686\u0687\u0688\u0689\u068a\u068b\u068c\u068d\u068e\u068f\u0690\u0691\u0692\u0693\u0694\u0695\u0696\u0697\u0698\u0699\u069a\u069b\u069c\u069d\u069e\u069f\u06a0\u06a1\u06a2\u06a3\u06a4\u06a5\u06a6\u06a7\u06a8\u06a9\u06aa\u06ab\u06ac\u06ad\u06ae\u06af\u06b0\u06b1\u06b2\u06b3\u06b4\u06b5\u06b6\u06b7\u06b8\u06b9\u06ba\u06bb\u06bc\u06bd\u06be\u06bf\u06c0\u06c1\u06c2\u06c3\u06c4\u06c5\u06c6\u06c7\u06c8\u06c9\u06ca\u06cb\u06cc\u06cd\u06ce\u06cf\u06d0\u06d1\u06d2\u06d3\u06d5\u06ee\u06ef\u06fa\u06fb\u06fc\u06ff\u0710\u0712\u0713\u0714\u0715\u0716\u0717\u0718\u0719\u071a\u071b\u071c\u071d\u071e\u071f\u0720\u0721\u0722\u0723\u0724\u0725\u0726\u0727\u0728\u0729\u072a\u072b\u072c\u072d\u072e\u072f\u074d\u074e\u074f\u0750\u0751\u0752\u0753\u0754\u0755\u0756\u0757\u0758\u0759\u075a\u075b\u075c\u075d\u075e\u075f\u0760\u0761\u0762\u0763\u0764\u0765\u0766\u0767\u0768\u0769\u076a\u076b\u076c\u076d\u0780\u0781\u0782\u0783\u0784\u0785\u0786\u0787\u0788\u0789\u078a\u078b\u078c\u078d\u078e\u078f\u0790\u0791\u0792\u0793\u0794\u0795\u0796\u0797\u0798\u0799\u079a\u079b\u079c\u079d\u079e\u079f\u07a0\u07a1\u07a2\u07a3\u07a4\u07a5\u07b1\u0904\u0905\u0906\u0907\u0908\u0909\u090a\u090b\u090c\u090d\u090e\u090f\u0910\u0911\u0912\u0913\u0914\u0915\u0916\u0917\u0918\u0919\u091a\u091b\u091c\u091d\u091e\u091f\u0920\u0921\u0922\u0923\u0924\u0925\u0926\u0927\u0928\u0929\u092a\u092b\u092c\u092d\u092e\u092f\u0930\u0931\u0932\u0933\u0934\u0935\u0936\u0937\u0938\u0939\u093d\u0950\u0958\u0959\u095a\u095b\u095c\u095d\u095e\u095f\u0960\u0961\u097d\u0985\u0986\u0987\u0988\u0989\u098a\u098b\u098c\u098f\u0990\u0993\u0994\u0995\u0996\u0997\u0998\u0999\u099a\u099b\u099c\u099d\u099e\u099f\u09a0\u09a1\u09a2\u09a3\u09a4\u09a5\u09a6\u09a7\u09a8\u09aa\u09ab\u09ac\u09ad\u09ae\u09af\u09b0\u09b2\u09b6\u09b7\u09b8\u09b9\u09bd\u09ce\u09dc\u09dd\u09df\u09e0\u09e1\u09f0\u09f1\u0a05\u0a06\u0a07\u0a08\u0a09\u0a0a\u0a0f\u0a10\u0a13\u0a14\u0a15\u0a16\u0a17\u0a18\u0a19\u0a1a\u0a1b\u0a1c\u0a1d\u0a1e\u0a1f\u0a20\u0a21\u0a22\u0a23\u0a24\u0a25\u0a26\u0a27\u0a28\u0a2a\u0a2b\u0a2c\u0a2d\u0a2e\u0a2f\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59\u0a5a\u0a5b\u0a5c\u0a5e\u0a72\u0a73\u0a74\u0a85\u0a86\u0a87\u0a88\u0a89\u0a8a\u0a8b\u0a8c\u0a8d\u0a8f\u0a90\u0a91\u0a93\u0a94\u0a95\u0a96\u0a97\u0a98\u0a99\u0a9a\u0a9b\u0a9c\u0a9d\u0a9e\u0a9f\u0aa0\u0aa1\u0aa2\u0aa3\u0aa4\u0aa5\u0aa6\u0aa7\u0aa8\u0aaa\u0aab\u0aac\u0aad\u0aae\u0aaf\u0ab0\u0ab2\u0ab3\u0ab5\u0ab6\u0ab7\u0ab8\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\u0b0c\u0b0f\u0b10\u0b13\u0b14\u0b15\u0b16\u0b17\u0b18\u0b19\u0b1a\u0b1b\u0b1c\u0b1d\u0b1e\u0b1f\u0b20\u0b21\u0b22\u0b23\u0b24\u0b25\u0b26\u0b27\u0b28\u0b2a\u0b2b\u0b2c\u0b2d\u0b2e\u0b2f\u0b30\u0b32\u0b33\u0b35\u0b36\u0b37\u0b38\u0b39\u0b3d\u0b5c\u0b5d\u0b5f\u0b60\u0b61\u0b71\u0b83\u0b85\u0b86\u0b87\u0b88\u0b89\u0b8a\u0b8e\u0b8f\u0b90\u0b92\u0b93\u0b94\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8\u0ba9\u0baa\u0bae\u0baf\u0bb0\u0bb1\u0bb2\u0bb3\u0bb4\u0bb5\u0bb6\u0bb7\u0bb8\u0bb9\u0c05\u0c06\u0c07\u0c08\u0c09\u0c0a\u0c0b\u0c0c\u0c0e\u0c0f\u0c10\u0c12\u0c13\u0c14\u0c15\u0c16\u0c17\u0c18\u0c19\u0c1a\u0c1b\u0c1c\u0c1d\u0c1e\u0c1f\u0c20\u0c21\u0c22\u0c23\u0c24\u0c25\u0c26\u0c27\u0c28\u0c2a\u0c2b\u0c2c\u0c2d\u0c2e\u0c2f\u0c30\u0c31\u0c32\u0c33\u0c35\u0c36\u0c37\u0c38\u0c39\u0c60\u0c61\u0c85\u0c86\u0c87\u0c88\u0c89\u0c8a\u0c8b\u0c8c\u0c8e\u0c8f\u0c90\u0c92\u0c93\u0c94\u0c95\u0c96\u0c97\u0c98\u0c99\u0c9a\u0c9b\u0c9c\u0c9d\u0c9e\u0c9f\u0ca0\u0ca1\u0ca2\u0ca3\u0ca4\u0ca5\u0ca6\u0ca7\u0ca8\u0caa\u0cab\u0cac\u0cad\u0cae\u0caf\u0cb0\u0cb1\u0cb2\u0cb3\u0cb5\u0cb6\u0cb7\u0cb8\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0d05\u0d06\u0d07\u0d08\u0d09\u0d0a\u0d0b\u0d0c\u0d0e\u0d0f\u0d10\u0d12\u0d13\u0d14\u0d15\u0d16\u0d17\u0d18\u0d19\u0d1a\u0d1b\u0d1c\u0d1d\u0d1e\u0d1f\u0d20\u0d21\u0d22\u0d23\u0d24\u0d25\u0d26\u0d27\u0d28\u0d2a\u0d2b\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36\u0d37\u0d38\u0d39\u0d60\u0d61\u0d85\u0d86\u0d87\u0d88\u0d89\u0d8a\u0d8b\u0d8c\u0d8d\u0d8e\u0d8f\u0d90\u0d91\u0d92\u0d93\u0d94\u0d95\u0d96\u0d9a\u0d9b\u0d9c\u0d9d\u0d9e\u0d9f\u0da0\u0da1\u0da2\u0da3\u0da4\u0da5\u0da6\u0da7\u0da8\u0da9\u0daa\u0dab\u0dac\u0dad\u0dae\u0daf\u0db0\u0db1\u0db3\u0db4\u0db5\u0db6\u0db7\u0db8\u0db9\u0dba\u0dbb\u0dbd\u0dc0\u0dc1\u0dc2\u0dc3\u0dc4\u0dc5\u0dc6\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e32\u0e33\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94\u0e95\u0e96\u0e97\u0e99\u0e9a\u0e9b\u0e9c\u0e9d\u0e9e\u0e9f\u0ea1\u0ea2\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead\u0eae\u0eaf\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0\u0ec1\u0ec2\u0ec3\u0ec4\u0edc\u0edd\u0f00\u0f40\u0f41\u0f42\u0f43\u0f44\u0f45\u0f46\u0f47\u0f49\u0f4a\u0f4b\u0f4c\u0f4d\u0f4e\u0f4f\u0f50\u0f51\u0f52\u0f53\u0f54\u0f55\u0f56\u0f57\u0f58\u0f59\u0f5a\u0f5b\u0f5c\u0f5d\u0f5e\u0f5f\u0f60\u0f61\u0f62\u0f63\u0f64\u0f65\u0f66\u0f67\u0f68\u0f69\u0f6a\u0f88\u0f89\u0f8a\u0f8b\u1000\u1001\u1002\u1003\u1004\u1005\u1006\u1007\u1008\u1009\u100a\u100b\u100c\u100d\u100e\u100f\u1010\u1011\u1012\u1013\u1014\u1015\u1016\u1017\u1018\u1019\u101a\u101b\u101c\u101d\u101e\u101f\u1020\u1021\u1023\u1024\u1025\u1026\u1027\u1029\u102a\u1050\u1051\u1052\u1053\u1054\u1055\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6\u10f7\u10f8\u10f9\u10fa\u1100\u1101\u1102\u1103\u1104\u1105\u1106\u1107\u1108\u1109\u110a\u110b\u110c\u110d\u110e\u110f\u1110\u1111\u1112\u1113\u1114\u1115\u1116\u1117\u1118\u1119\u111a\u111b\u111c\u111d\u111e\u111f\u1120\u1121\u1122\u1123\u1124\u1125\u1126\u1127\u1128\u1129\u112a\u112b\u112c\u112d\u112e\u112f\u1130\u1131\u1132\u1133\u1134\u1135\u1136\u1137\u1138\u1139\u113a\u113b\u113c\u113d\u113e\u113f\u1140\u1141\u1142\u1143\u1144\u1145\u1146\u1147\u1148\u1149\u114a\u114b\u114c\u114d\u114e\u114f\u1150\u1151\u1152\u1153\u1154\u1155\u1156\u1157\u1158\u1159\u115f\u1160\u1161\u1162\u1163\u1164\u1165\u1166\u1167\u1168\u1169\u116a\u116b\u116c\u116d\u116e\u116f\u1170\u1171\u1172\u1173\u1174\u1175\u1176\u1177\u1178\u1179\u117a\u117b\u117c\u117d\u117e\u117f\u1180\u1181\u1182\u1183\u1184\u1185\u1186\u1187\u1188\u1189\u118a\u118b\u118c\u118d\u118e\u118f\u1190\u1191\u1192\u1193\u1194\u1195\u1196\u1197\u1198\u1199\u119a\u119b\u119c\u119d\u119e\u119f\u11a0\u11a1\u11a2\u11a8\u11a9\u11aa\u11ab\u11ac\u11ad\u11ae\u11af\u11b0\u11b1\u11b2\u11b3\u11b4\u11b5\u11b6\u11b7\u11b8\u11b9\u11ba\u11bb\u11bc\u11bd\u11be\u11bf\u11c0\u11c1\u11c2\u11c3\u11c4\u11c5\u11c6\u11c7\u11c8\u11c9\u11ca\u11cb\u11cc\u11cd\u11ce\u11cf\u11d0\u11d1\u11d2\u11d3\u11d4\u11d5\u11d6\u11d7\u11d8\u11d9\u11da\u11db\u11dc\u11dd\u11de\u11df\u11e0\u11e1\u11e2\u11e3\u11e4\u11e5\u11e6\u11e7\u11e8\u11e9\u11ea\u11eb\u11ec\u11ed\u11ee\u11ef\u11f0\u11f1\u11f2\u11f3\u11f4\u11f5\u11f6\u11f7\u11f8\u11f9\u1200\u1201\u1202\u1203\u1204\u1205\u1206\u1207\u1208\u1209\u120a\u120b\u120c\u120d\u120e\u120f\u1210\u1211\u1212\u1213\u1214\u1215\u1216\u1217\u1218\u1219\u121a\u121b\u121c\u121d\u121e\u121f\u1220\u1221\u1222\u1223\u1224\u1225\u1226\u1227\u1228\u1229\u122a\u122b\u122c\u122d\u122e\u122f\u1230\u1231\u1232\u1233\u1234\u1235\u1236\u1237\u1238\u1239\u123a\u123b\u123c\u123d\u123e\u123f\u1240\u1241\u1242\u1243\u1244\u1245\u1246\u1247\u1248\u124a\u124b\u124c\u124d\u1250\u1251\u1252\u1253\u1254\u1255\u1256\u1258\u125a\u125b\u125c\u125d\u1260\u1261\u1262\u1263\u1264\u1265\u1266\u1267\u1268\u1269\u126a\u126b\u126c\u126d\u126e\u126f\u1270\u1271\u1272\u1273\u1274\u1275\u1276\u1277\u1278\u1279\u127a\u127b\u127c\u127d\u127e\u127f\u1280\u1281\u1282\u1283\u1284\u1285\u1286\u1287\u1288\u128a\u128b\u128c\u128d\u1290\u1291\u1292\u1293\u1294\u1295\u1296\u1297\u1298\u1299\u129a\u129b\u129c\u129d\u129e\u129f\u12a0\u12a1\u12a2\u12a3\u12a4\u12a5\u12a6\u12a7\u12a8\u12a9\u12aa\u12ab\u12ac\u12ad\u12ae\u12af\u12b0\u12b2\u12b3\u12b4\u12b5\u12b8\u12b9\u12ba\u12bb\u12bc\u12bd\u12be\u12c0\u12c2\u12c3\u12c4\u12c5\u12c8\u12c9\u12ca\u12cb\u12cc\u12cd\u12ce\u12cf\u12d0\u12d1\u12d2\u12d3\u12d4\u12d5\u12d6\u12d8\u12d9\u12da\u12db\u12dc\u12dd\u12de\u12df\u12e0\u12e1\u12e2\u12e3\u12e4\u12e5\u12e6\u12e7\u12e8\u12e9\u12ea\u12eb\u12ec\u12ed\u12ee\u12ef\u12f0\u12f1\u12f2\u12f3\u12f4\u12f5\u12f6\u12f7\u12f8\u12f9\u12fa\u12fb\u12fc\u12fd\u12fe\u12ff\u1300\u1301\u1302\u1303\u1304\u1305\u1306\u1307\u1308\u1309\u130a\u130b\u130c\u130d\u130e\u130f\u1310\u1312\u1313\u1314\u1315\u1318\u1319\u131a\u131b\u131c\u131d\u131e\u131f\u1320\u1321\u1322\u1323\u1324\u1325\u1326\u1327\u1328\u1329\u132a\u132b\u132c\u132d\u132e\u132f\u1330\u1331\u1332\u1333\u1334\u1335\u1336\u1337\u1338\u1339\u133a\u133b\u133c\u133d\u133e\u133f\u1340\u1341\u1342\u1343\u1344\u1345\u1346\u1347\u1348\u1349\u134a\u134b\u134c\u134d\u134e\u134f\u1350\u1351\u1352\u1353\u1354\u1355\u1356\u1357\u1358\u1359\u135a\u1380\u1381\u1382\u1383\u1384\u1385\u1386\u1387\u1388\u1389\u138a\u138b\u138c\u138d\u138e\u138f\u13a0\u13a1\u13a2\u13a3\u13a4\u13a5\u13a6\u13a7\u13a8\u13a9\u13aa\u13ab\u13ac\u13ad\u13ae\u13af\u13b0\u13b1\u13b2\u13b3\u13b4\u13b5\u13b6\u13b7\u13b8\u13b9\u13ba\u13bb\u13bc\u13bd\u13be\u13bf\u13c0\u13c1\u13c2\u13c3\u13c4\u13c5\u13c6\u13c7\u13c8\u13c9\u13ca\u13cb\u13cc\u13cd\u13ce\u13cf\u13d0\u13d1\u13d2\u13d3\u13d4\u13d5\u13d6\u13d7\u13d8\u13d9\u13da\u13db\u13dc\u13dd\u13de\u13df\u13e0\u13e1\u13e2\u13e3\u13e4\u13e5\u13e6\u13e7\u13e8\u13e9\u13ea\u13eb\u13ec\u13ed\u13ee\u13ef\u13f0\u13f1\u13f2\u13f3\u13f4\u1401\u1402\u1403\u1404\u1405\u1406\u1407\u1408\u1409\u140a\u140b\u140c\u140d\u140e\u140f\u1410\u1411\u1412\u1413\u1414\u1415\u1416\u1417\u1418\u1419\u141a\u141b\u141c\u141d\u141e\u141f\u1420\u1421\u1422\u1423\u1424\u1425\u1426\u1427\u1428\u1429\u142a\u142b\u142c\u142d\u142e\u142f\u1430\u1431\u1432\u1433\u1434\u1435\u1436\u1437\u1438\u1439\u143a\u143b\u143c\u143d\u143e\u143f\u1440\u1441\u1442\u1443\u1444\u1445\u1446\u1447\u1448\u1449\u144a\u144b\u144c\u144d\u144e\u144f\u1450\u1451\u1452\u1453\u1454\u1455\u1456\u1457\u1458\u1459\u145a\u145b\u145c\u145d\u145e\u145f\u1460\u1461\u1462\u1463\u1464\u1465\u1466\u1467\u1468\u1469\u146a\u146b\u146c\u146d\u146e\u146f\u1470\u1471\u1472\u1473\u1474\u1475\u1476\u1477\u1478\u1479\u147a\u147b\u147c\u147d\u147e\u147f\u1480\u1481\u1482\u1483\u1484\u1485\u1486\u1487\u1488\u1489\u148a\u148b\u148c\u148d\u148e\u148f\u1490\u1491\u1492\u1493\u1494\u1495\u1496\u1497\u1498\u1499\u149a\u149b\u149c\u149d\u149e\u149f\u14a0\u14a1\u14a2\u14a3\u14a4\u14a5\u14a6\u14a7\u14a8\u14a9\u14aa\u14ab\u14ac\u14ad\u14ae\u14af\u14b0\u14b1\u14b2\u14b3\u14b4\u14b5\u14b6\u14b7\u14b8\u14b9\u14ba\u14bb\u14bc\u14bd\u14be\u14bf\u14c0\u14c1\u14c2\u14c3\u14c4\u14c5\u14c6\u14c7\u14c8\u14c9\u14ca\u14cb\u14cc\u14cd\u14ce\u14cf\u14d0\u14d1\u14d2\u14d3\u14d4\u14d5\u14d6\u14d7\u14d8\u14d9\u14da\u14db\u14dc\u14dd\u14de\u14df\u14e0\u14e1\u14e2\u14e3\u14e4\u14e5\u14e6\u14e7\u14e8\u14e9\u14ea\u14eb\u14ec\u14ed\u14ee\u14ef\u14f0\u14f1\u14f2\u14f3\u14f4\u14f5\u14f6\u14f7\u14f8\u14f9\u14fa\u14fb\u14fc\u14fd\u14fe\u14ff\u1500\u1501\u1502\u1503\u1504\u1505\u1506\u1507\u1508\u1509\u150a\u150b\u150c\u150d\u150e\u150f\u1510\u1511\u1512\u1513\u1514\u1515\u1516\u1517\u1518\u1519\u151a\u151b\u151c\u151d\u151e\u151f\u1520\u1521\u1522\u1523\u1524\u1525\u1526\u1527\u1528\u1529\u152a\u152b\u152c\u152d\u152e\u152f\u1530\u1531\u1532\u1533\u1534\u1535\u1536\u1537\u1538\u1539\u153a\u153b\u153c\u153d\u153e\u153f\u1540\u1541\u1542\u1543\u1544\u1545\u1546\u1547\u1548\u1549\u154a\u154b\u154c\u154d\u154e\u154f\u1550\u1551\u1552\u1553\u1554\u1555\u1556\u1557\u1558\u1559\u155a\u155b\u155c\u155d\u155e\u155f\u1560\u1561\u1562\u1563\u1564\u1565\u1566\u1567\u1568\u1569\u156a\u156b\u156c\u156d\u156e\u156f\u1570\u1571\u1572\u1573\u1574\u1575\u1576\u1577\u1578\u1579\u157a\u157b\u157c\u157d\u157e\u157f\u1580\u1581\u1582\u1583\u1584\u1585\u1586\u1587\u1588\u1589\u158a\u158b\u158c\u158d\u158e\u158f\u1590\u1591\u1592\u1593\u1594\u1595\u1596\u1597\u1598\u1599\u159a\u159b\u159c\u159d\u159e\u159f\u15a0\u15a1\u15a2\u15a3\u15a4\u15a5\u15a6\u15a7\u15a8\u15a9\u15aa\u15ab\u15ac\u15ad\u15ae\u15af\u15b0\u15b1\u15b2\u15b3\u15b4\u15b5\u15b6\u15b7\u15b8\u15b9\u15ba\u15bb\u15bc\u15bd\u15be\u15bf\u15c0\u15c1\u15c2\u15c3\u15c4\u15c5\u15c6\u15c7\u15c8\u15c9\u15ca\u15cb\u15cc\u15cd\u15ce\u15cf\u15d0\u15d1\u15d2\u15d3\u15d4\u15d5\u15d6\u15d7\u15d8\u15d9\u15da\u15db\u15dc\u15dd\u15de\u15df\u15e0\u15e1\u15e2\u15e3\u15e4\u15e5\u15e6\u15e7\u15e8\u15e9\u15ea\u15eb\u15ec\u15ed\u15ee\u15ef\u15f0\u15f1\u15f2\u15f3\u15f4\u15f5\u15f6\u15f7\u15f8\u15f9\u15fa\u15fb\u15fc\u15fd\u15fe\u15ff\u1600\u1601\u1602\u1603\u1604\u1605\u1606\u1607\u1608\u1609\u160a\u160b\u160c\u160d\u160e\u160f\u1610\u1611\u1612\u1613\u1614\u1615\u1616\u1617\u1618\u1619\u161a\u161b\u161c\u161d\u161e\u161f\u1620\u1621\u1622\u1623\u1624\u1625\u1626\u1627\u1628\u1629\u162a\u162b\u162c\u162d\u162e\u162f\u1630\u1631\u1632\u1633\u1634\u1635\u1636\u1637\u1638\u1639\u163a\u163b\u163c\u163d\u163e\u163f\u1640\u1641\u1642\u1643\u1644\u1645\u1646\u1647\u1648\u1649\u164a\u164b\u164c\u164d\u164e\u164f\u1650\u1651\u1652\u1653\u1654\u1655\u1656\u1657\u1658\u1659\u165a\u165b\u165c\u165d\u165e\u165f\u1660\u1661\u1662\u1663\u1664\u1665\u1666\u1667\u1668\u1669\u166a\u166b\u166c\u166f\u1670\u1671\u1672\u1673\u1674\u1675\u1676\u1681\u1682\u1683\u1684\u1685\u1686\u1687\u1688\u1689\u168a\u168b\u168c\u168d\u168e\u168f\u1690\u1691\u1692\u1693\u1694\u1695\u1696\u1697\u1698\u1699\u169a\u16a0\u16a1\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af\u16b0\u16b1\u16b2\u16b3\u16b4\u16b5\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u1700\u1701\u1702\u1703\u1704\u1705\u1706\u1707\u1708\u1709\u170a\u170b\u170c\u170e\u170f\u1710\u1711\u1720\u1721\u1722\u1723\u1724\u1725\u1726\u1727\u1728\u1729\u172a\u172b\u172c\u172d\u172e\u172f\u1730\u1731\u1740\u1741\u1742\u1743\u1744\u1745\u1746\u1747\u1748\u1749\u174a\u174b\u174c\u174d\u174e\u174f\u1750\u1751\u1760\u1761\u1762\u1763\u1764\u1765\u1766\u1767\u1768\u1769\u176a\u176b\u176c\u176e\u176f\u1770\u1780\u1781\u1782\u1783\u1784\u1785\u1786\u1787\u1788\u1789\u178a\u178b\u178c\u178d\u178e\u178f\u1790\u1791\u1792\u1793\u1794\u1795\u1796\u1797\u1798\u1799\u179a\u179b\u179c\u179d\u179e\u179f\u17a0\u17a1\u17a2\u17a3\u17a4\u17a5\u17a6\u17a7\u17a8\u17a9\u17aa\u17ab\u17ac\u17ad\u17ae\u17af\u17b0\u17b1\u17b2\u17b3\u17dc\u1820\u1821\u1822\u1823\u1824\u1825\u1826\u1827\u1828\u1829\u182a\u182b\u182c\u182d\u182e\u182f\u1830\u1831\u1832\u1833\u1834\u1835\u1836\u1837\u1838\u1839\u183a\u183b\u183c\u183d\u183e\u183f\u1840\u1841\u1842\u1844\u1845\u1846\u1847\u1848\u1849\u184a\u184b\u184c\u184d\u184e\u184f\u1850\u1851\u1852\u1853\u1854\u1855\u1856\u1857\u1858\u1859\u185a\u185b\u185c\u185d\u185e\u185f\u1860\u1861\u1862\u1863\u1864\u1865\u1866\u1867\u1868\u1869\u186a\u186b\u186c\u186d\u186e\u186f\u1870\u1871\u1872\u1873\u1874\u1875\u1876\u1877\u1880\u1881\u1882\u1883\u1884\u1885\u1886\u1887\u1888\u1889\u188a\u188b\u188c\u188d\u188e\u188f\u1890\u1891\u1892\u1893\u1894\u1895\u1896\u1897\u1898\u1899\u189a\u189b\u189c\u189d\u189e\u189f\u18a0\u18a1\u18a2\u18a3\u18a4\u18a5\u18a6\u18a7\u18a8\u1900\u1901\u1902\u1903\u1904\u1905\u1906\u1907\u1908\u1909\u190a\u190b\u190c\u190d\u190e\u190f\u1910\u1911\u1912\u1913\u1914\u1915\u1916\u1917\u1918\u1919\u191a\u191b\u191c\u1950\u1951\u1952\u1953\u1954\u1955\u1956\u1957\u1958\u1959\u195a\u195b\u195c\u195d\u195e\u195f\u1960\u1961\u1962\u1963\u1964\u1965\u1966\u1967\u1968\u1969\u196a\u196b\u196c\u196d\u1970\u1971\u1972\u1973\u1974\u1980\u1981\u1982\u1983\u1984\u1985\u1986\u1987\u1988\u1989\u198a\u198b\u198c\u198d\u198e\u198f\u1990\u1991\u1992\u1993\u1994\u1995\u1996\u1997\u1998\u1999\u199a\u199b\u199c\u199d\u199e\u199f\u19a0\u19a1\u19a2\u19a3\u19a4\u19a5\u19a6\u19a7\u19a8\u19a9\u19c1\u19c2\u19c3\u19c4\u19c5\u19c6\u19c7\u1a00\u1a01\u1a02\u1a03\u1a04\u1a05\u1a06\u1a07\u1a08\u1a09\u1a0a\u1a0b\u1a0c\u1a0d\u1a0e\u1a0f\u1a10\u1a11\u1a12\u1a13\u1a14\u1a15\u1a16\u2135\u2136\u2137\u2138\u2d30\u2d31\u2d32\u2d33\u2d34\u2d35\u2d36\u2d37\u2d38\u2d39\u2d3a\u2d3b\u2d3c\u2d3d\u2d3e\u2d3f\u2d40\u2d41\u2d42\u2d43\u2d44\u2d45\u2d46\u2d47\u2d48\u2d49\u2d4a\u2d4b\u2d4c\u2d4d\u2d4e\u2d4f\u2d50\u2d51\u2d52\u2d53\u2d54\u2d55\u2d56\u2d57\u2d58\u2d59\u2d5a\u2d5b\u2d5c\u2d5d\u2d5e\u2d5f\u2d60\u2d61\u2d62\u2d63\u2d64\u2d65\u2d80\u2d81\u2d82\u2d83\u2d84\u2d85\u2d86\u2d87\u2d88\u2d89\u2d8a\u2d8b\u2d8c\u2d8d\u2d8e\u2d8f\u2d90\u2d91\u2d92\u2d93\u2d94\u2d95\u2d96\u2da0\u2da1\u2da2\u2da3\u2da4\u2da5\u2da6\u2da8\u2da9\u2daa\u2dab\u2dac\u2dad\u2dae\u2db0\u2db1\u2db2\u2db3\u2db4\u2db5\u2db6\u2db8\u2db9\u2dba\u2dbb\u2dbc\u2dbd\u2dbe\u2dc0\u2dc1\u2dc2\u2dc3\u2dc4\u2dc5\u2dc6\u2dc8\u2dc9\u2dca\u2dcb\u2dcc\u2dcd\u2dce\u2dd0\u2dd1\u2dd2\u2dd3\u2dd4\u2dd5\u2dd6\u2dd8\u2dd9\u2dda\u2ddb\u2ddc\u2ddd\u2dde\u3006\u303c\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048\u3049\u304a\u304b\u304c\u304d\u304e\u304f\u3050\u3051\u3052\u3053\u3054\u3055\u3056\u3057\u3058\u3059\u305a\u305b\u305c\u305d\u305e\u305f\u3060\u3061\u3062\u3063\u3064\u3065\u3066\u3067\u3068\u3069\u306a\u306b\u306c\u306d\u306e\u306f\u3070\u3071\u3072\u3073\u3074\u3075\u3076\u3077\u3078\u3079\u307a\u307b\u307c\u307d\u307e\u307f\u3080\u3081\u3082\u3083\u3084\u3085\u3086\u3087\u3088\u3089\u308a\u308b\u308c\u308d\u308e\u308f\u3090\u3091\u3092\u3093\u3094\u3095\u3096\u309f\u30a1\u30a2\u30a3\u30a4\u30a5\u30a6\u30a7\u30a8\u30a9\u30aa\u30ab\u30ac\u30ad\u30ae\u30af\u30b0\u30b1\u30b2\u30b3\u30b4\u30b5\u30b6\u30b7\u30b8\u30b9\u30ba\u30bb\u30bc\u30bd\u30be\u30bf\u30c0\u30c1\u30c2\u30c3\u30c4\u30c5\u30c6\u30c7\u30c8\u30c9\u30ca\u30cb\u30cc\u30cd\u30ce\u30cf\u30d0\u30d1\u30d2\u30d3\u30d4\u30d5\u30d6\u30d7\u30d8\u30d9\u30da\u30db\u30dc\u30dd\u30de\u30df\u30e0\u30e1\u30e2\u30e3\u30e4\u30e5\u30e6\u30e7\u30e8\u30e9\u30ea\u30eb\u30ec\u30ed\u30ee\u30ef\u30f0\u30f1\u30f2\u30f3\u30f4\u30f5\u30f6\u30f7\u30f8\u30f9\u30fa\u30ff\u3105\u3106\u3107\u3108\u3109\u310a\u310b\u310c\u310d\u310e\u310f\u3110\u3111\u3112\u3113\u3114\u3115\u3116\u3117\u3118\u3119\u311a\u311b\u311c\u311d\u311e\u311f\u3120\u3121\u3122\u3123\u3124\u3125\u3126\u3127\u3128\u3129\u312a\u312b\u312c\u3131\u3132\u3133\u3134\u3135\u3136\u3137\u3138\u3139\u313a\u313b\u313c\u313d\u313e\u313f\u3140\u3141\u3142\u3143\u3144\u3145\u3146\u3147\u3148\u3149\u314a\u314b\u314c\u314d\u314e\u314f\u3150\u3151\u3152\u3153\u3154\u3155\u3156\u3157\u3158\u3159\u315a\u315b\u315c\u315d\u315e\u315f\u3160\u3161\u3162\u3163\u3164\u3165\u3166\u3167\u3168\u3169\u316a\u316b\u316c\u316d\u316e\u316f\u3170\u3171\u3172\u3173\u3174\u3175\u3176\u3177\u3178\u3179\u317a\u317b\u317c\u317d\u317e\u317f\u3180\u3181\u3182\u3183\u3184\u3185\u3186\u3187\u3188\u3189\u318a\u318b\u318c\u318d\u318e\u31a0\u31a1\u31a2\u31a3\u31a4\u31a5\u31a6\u31a7\u31a8\u31a9\u31aa\u31ab\u31ac\u31ad\u31ae\u31af\u31b0\u31b1\u31b2\u31b3\u31b4\u31b5\u31b6\u31b7\u31f0\u31f1\u31f2\u31f3\u31f4\u31f5\u31f6\u31f7\u31f8\u31f9\u31fa\u31fb\u31fc\u31fd\u31fe\u31ff\u3400\u3401\u3402\u3403\u3404\u3405\u3406\u3407\u3408\u3409\u340a\u340b\u340c\u340d\u340e\u340f\u3410\u3411\u3412\u3413\u3414\u3415\u3416\u3417\u3418\u3419\u341a\u341b\u341c\u341d\u341e\u341f\u3420\u3421\u3422\u3423\u3424\u3425\u3426\u3427\u3428\u3429\u342a\u342b\u342c\u342d\u342e\u342f\u3430\u3431\u3432\u3433\u3434\u3435\u3436\u3437\u3438\u3439\u343a\u343b\u343c\u343d\u343e\u343f\u3440\u3441\u3442\u3443\u3444\u3445\u3446\u3447\u3448\u3449\u344a\u344b\u344c\u344d\u344e\u344f\u3450\u3451\u3452\u3453\u3454\u3455\u3456\u3457\u3458\u3459\u345a\u345b\u345c\u345d\u345e\u345f\u3460\u3461\u3462\u3463\u3464\u3465\u3466\u3467\u3468\u3469\u346a\u346b\u346c\u346d\u346e\u346f\u3470\u3471\u3472\u3473\u3474\u3475\u3476\u3477\u3478\u3479\u347a\u347b\u347c\u347d\u347e\u347f\u3480\u3481\u3482\u3483\u3484\u3485\u3486\u3487\u3488\u3489\u348a\u348b\u348c\u348d\u348e\u348f\u3490\u3491\u3492\u3493\u3494\u3495\u3496\u3497\u3498\u3499\u349a\u349b\u349c\u349d\u349e\u349f\u34a0\u34a1\u34a2\u34a3\u34a4\u34a5\u34a6\u34a7\u34a8\u34a9\u34aa\u34ab\u34ac\u34ad\u34ae\u34af\u34b0\u34b1\u34b2\u34b3\u34b4\u34b5\u34b6\u34b7\u34b8\u34b9\u34ba\u34bb\u34bc\u34bd\u34be\u34bf\u34c0\u34c1\u34c2\u34c3\u34c4\u34c5\u34c6\u34c7\u34c8\u34c9\u34ca\u34cb\u34cc\u34cd\u34ce\u34cf\u34d0\u34d1\u34d2\u34d3\u34d4\u34d5\u34d6\u34d7\u34d8\u34d9\u34da\u34db\u34dc\u34dd\u34de\u34df\u34e0\u34e1\u34e2\u34e3\u34e4\u34e5\u34e6\u34e7\u34e8\u34e9\u34ea\u34eb\u34ec\u34ed\u34ee\u34ef\u34f0\u34f1\u34f2\u34f3\u34f4\u34f5\u34f6\u34f7\u34f8\u34f9\u34fa\u34fb\u34fc\u34fd\u34fe\u34ff\u3500\u3501\u3502\u3503\u3504\u3505\u3506\u3507\u3508\u3509\u350a\u350b\u350c\u350d\u350e\u350f\u3510\u3511\u3512\u3513\u3514\u3515\u3516\u3517\u3518\u3519\u351a\u351b\u351c\u351d\u351e\u351f\u3520\u3521\u3522\u3523\u3524\u3525\u3526\u3527\u3528\u3529\u352a\u352b\u352c\u352d\u352e\u352f\u3530\u3531\u3532\u3533\u3534\u3535\u3536\u3537\u3538\u3539\u353a\u353b\u353c\u353d\u353e\u353f\u3540\u3541\u3542\u3543\u3544\u3545\u3546\u3547\u3548\u3549\u354a\u354b\u354c\u354d\u354e\u354f\u3550\u3551\u3552\u3553\u3554\u3555\u3556\u3557\u3558\u3559\u355a\u355b\u355c\u355d\u355e\u355f\u3560\u3561\u3562\u3563\u3564\u3565\u3566\u3567\u3568\u3569\u356a\u356b\u356c\u356d\u356e\u356f\u3570\u3571\u3572\u3573\u3574\u3575\u3576\u3577\u3578\u3579\u357a\u357b\u357c\u357d\u357e\u357f\u3580\u3581\u3582\u3583\u3584\u3585\u3586\u3587\u3588\u3589\u358a\u358b\u358c\u358d\u358e\u358f\u3590\u3591\u3592\u3593\u3594\u3595\u3596\u3597\u3598\u3599\u359a\u359b\u359c\u359d\u359e\u359f\u35a0\u35a1\u35a2\u35a3\u35a4\u35a5\u35a6\u35a7\u35a8\u35a9\u35aa\u35ab\u35ac\u35ad\u35ae\u35af\u35b0\u35b1\u35b2\u35b3\u35b4\u35b5\u35b6\u35b7\u35b8\u35b9\u35ba\u35bb\u35bc\u35bd\u35be\u35bf\u35c0\u35c1\u35c2\u35c3\u35c4\u35c5\u35c6\u35c7\u35c8\u35c9\u35ca\u35cb\u35cc\u35cd\u35ce\u35cf\u35d0\u35d1\u35d2\u35d3\u35d4\u35d5\u35d6\u35d7\u35d8\u35d9\u35da\u35db\u35dc\u35dd\u35de\u35df\u35e0\u35e1\u35e2\u35e3\u35e4\u35e5\u35e6\u35e7\u35e8\u35e9\u35ea\u35eb\u35ec\u35ed\u35ee\u35ef\u35f0\u35f1\u35f2\u35f3\u35f4\u35f5\u35f6\u35f7\u35f8\u35f9\u35fa\u35fb\u35fc\u35fd\u35fe\u35ff\u3600\u3601\u3602\u3603\u3604\u3605\u3606\u3607\u3608\u3609\u360a\u360b\u360c\u360d\u360e\u360f\u3610\u3611\u3612\u3613\u3614\u3615\u3616\u3617\u3618\u3619\u361a\u361b\u361c\u361d\u361e\u361f\u3620\u3621\u3622\u3623\u3624\u3625\u3626\u3627\u3628\u3629\u362a\u362b\u362c\u362d\u362e\u362f\u3630\u3631\u3632\u3633\u3634\u3635\u3636\u3637\u3638\u3639\u363a\u363b\u363c\u363d\u363e\u363f\u3640\u3641\u3642\u3643\u3644\u3645\u3646\u3647\u3648\u3649\u364a\u364b\u364c\u364d\u364e\u364f\u3650\u3651\u3652\u3653\u3654\u3655\u3656\u3657\u3658\u3659\u365a\u365b\u365c\u365d\u365e\u365f\u3660\u3661\u3662\u3663\u3664\u3665\u3666\u3667\u3668\u3669\u366a\u366b\u366c\u366d\u366e\u366f\u3670\u3671\u3672\u3673\u3674\u3675\u3676\u3677\u3678\u3679\u367a\u367b\u367c\u367d\u367e\u367f\u3680\u3681\u3682\u3683\u3684\u3685\u3686\u3687\u3688\u3689\u368a\u368b\u368c\u368d\u368e\u368f\u3690\u3691\u3692\u3693\u3694\u3695\u3696\u3697\u3698\u3699\u369a\u369b\u369c\u369d\u369e\u369f\u36a0\u36a1\u36a2\u36a3\u36a4\u36a5\u36a6\u36a7\u36a8\u36a9\u36aa\u36ab\u36ac\u36ad\u36ae\u36af\u36b0\u36b1\u36b2\u36b3\u36b4\u36b5\u36b6\u36b7\u36b8\u36b9\u36ba\u36bb\u36bc\u36bd\u36be\u36bf\u36c0\u36c1\u36c2\u36c3\u36c4\u36c5\u36c6\u36c7\u36c8\u36c9\u36ca\u36cb\u36cc\u36cd\u36ce\u36cf\u36d0\u36d1\u36d2\u36d3\u36d4\u36d5\u36d6\u36d7\u36d8\u36d9\u36da\u36db\u36dc\u36dd\u36de\u36df\u36e0\u36e1\u36e2\u36e3\u36e4\u36e5\u36e6\u36e7\u36e8\u36e9\u36ea\u36eb\u36ec\u36ed\u36ee\u36ef\u36f0\u36f1\u36f2\u36f3\u36f4\u36f5\u36f6\u36f7\u36f8\u36f9\u36fa\u36fb\u36fc\u36fd\u36fe\u36ff\u3700\u3701\u3702\u3703\u3704\u3705\u3706\u3707\u3708\u3709\u370a\u370b\u370c\u370d\u370e\u370f\u3710\u3711\u3712\u3713\u3714\u3715\u3716\u3717\u3718\u3719\u371a\u371b\u371c\u371d\u371e\u371f\u3720\u3721\u3722\u3723\u3724\u3725\u3726\u3727\u3728\u3729\u372a\u372b\u372c\u372d\u372e\u372f\u3730\u3731\u3732\u3733\u3734\u3735\u3736\u3737\u3738\u3739\u373a\u373b\u373c\u373d\u373e\u373f\u3740\u3741\u3742\u3743\u3744\u3745\u3746\u3747\u3748\u3749\u374a\u374b\u374c\u374d\u374e\u374f\u3750\u3751\u3752\u3753\u3754\u3755\u3756\u3757\u3758\u3759\u375a\u375b\u375c\u375d\u375e\u375f\u3760\u3761\u3762\u3763\u3764\u3765\u3766\u3767\u3768\u3769\u376a\u376b\u376c\u376d\u376e\u376f\u3770\u3771\u3772\u3773\u3774\u3775\u3776\u3777\u3778\u3779\u377a\u377b\u377c\u377d\u377e\u377f\u3780\u3781\u3782\u3783\u3784\u3785\u3786\u3787\u3788\u3789\u378a\u378b\u378c\u378d\u378e\u378f\u3790\u3791\u3792\u3793\u3794\u3795\u3796\u3797\u3798\u3799\u379a\u379b\u379c\u379d\u379e\u379f\u37a0\u37a1\u37a2\u37a3\u37a4\u37a5\u37a6\u37a7\u37a8\u37a9\u37aa\u37ab\u37ac\u37ad\u37ae\u37af\u37b0\u37b1\u37b2\u37b3\u37b4\u37b5\u37b6\u37b7\u37b8\u37b9\u37ba\u37bb\u37bc\u37bd\u37be\u37bf\u37c0\u37c1\u37c2\u37c3\u37c4\u37c5\u37c6\u37c7\u37c8\u37c9\u37ca\u37cb\u37cc\u37cd\u37ce\u37cf\u37d0\u37d1\u37d2\u37d3\u37d4\u37d5\u37d6\u37d7\u37d8\u37d9\u37da\u37db\u37dc\u37dd\u37de\u37df\u37e0\u37e1\u37e2\u37e3\u37e4\u37e5\u37e6\u37e7\u37e8\u37e9\u37ea\u37eb\u37ec\u37ed\u37ee\u37ef\u37f0\u37f1\u37f2\u37f3\u37f4\u37f5\u37f6\u37f7\u37f8\u37f9\u37fa\u37fb\u37fc\u37fd\u37fe\u37ff\u3800\u3801\u3802\u3803\u3804\u3805\u3806\u3807\u3808\u3809\u380a\u380b\u380c\u380d\u380e\u380f\u3810\u3811\u3812\u3813\u3814\u3815\u3816\u3817\u3818\u3819\u381a\u381b\u381c\u381d\u381e\u381f\u3820\u3821\u3822\u3823\u3824\u3825\u3826\u3827\u3828\u3829\u382a\u382b\u382c\u382d\u382e\u382f\u3830\u3831\u3832\u3833\u3834\u3835\u3836\u3837\u3838\u3839\u383a\u383b\u383c\u383d\u383e\u383f\u3840\u3841\u3842\u3843\u3844\u3845\u3846\u3847\u3848\u3849\u384a\u384b\u384c\u384d\u384e\u384f\u3850\u3851\u3852\u3853\u3854\u3855\u3856\u3857\u3858\u3859\u385a\u385b\u385c\u385d\u385e\u385f\u3860\u3861\u3862\u3863\u3864\u3865\u3866\u3867\u3868\u3869\u386a\u386b\u386c\u386d\u386e\u386f\u3870\u3871\u3872\u3873\u3874\u3875\u3876\u3877\u3878\u3879\u387a\u387b\u387c\u387d\u387e\u387f\u3880\u3881\u3882\u3883\u3884\u3885\u3886\u3887\u3888\u3889\u388a\u388b\u388c\u388d\u388e\u388f\u3890\u3891\u3892\u3893\u3894\u3895\u3896\u3897\u3898\u3899\u389a\u389b\u389c\u389d\u389e\u389f\u38a0\u38a1\u38a2\u38a3\u38a4\u38a5\u38a6\u38a7\u38a8\u38a9\u38aa\u38ab\u38ac\u38ad\u38ae\u38af\u38b0\u38b1\u38b2\u38b3\u38b4\u38b5\u38b6\u38b7\u38b8\u38b9\u38ba\u38bb\u38bc\u38bd\u38be\u38bf\u38c0\u38c1\u38c2\u38c3\u38c4\u38c5\u38c6\u38c7\u38c8\u38c9\u38ca\u38cb\u38cc\u38cd\u38ce\u38cf\u38d0\u38d1\u38d2\u38d3\u38d4\u38d5\u38d6\u38d7\u38d8\u38d9\u38da\u38db\u38dc\u38dd\u38de\u38df\u38e0\u38e1\u38e2\u38e3\u38e4\u38e5\u38e6\u38e7\u38e8\u38e9\u38ea\u38eb\u38ec\u38ed\u38ee\u38ef\u38f0\u38f1\u38f2\u38f3\u38f4\u38f5\u38f6\u38f7\u38f8\u38f9\u38fa\u38fb\u38fc\u38fd\u38fe\u38ff\u3900\u3901\u3902\u3903\u3904\u3905\u3906\u3907\u3908\u3909\u390a\u390b\u390c\u390d\u390e\u390f\u3910\u3911\u3912\u3913\u3914\u3915\u3916\u3917\u3918\u3919\u391a\u391b\u391c\u391d\u391e\u391f\u3920\u3921\u3922\u3923\u3924\u3925\u3926\u3927\u3928\u3929\u392a\u392b\u392c\u392d\u392e\u392f\u3930\u3931\u3932\u3933\u3934\u3935\u3936\u3937\u3938\u3939\u393a\u393b\u393c\u393d\u393e\u393f\u3940\u3941\u3942\u3943\u3944\u3945\u3946\u3947\u3948\u3949\u394a\u394b\u394c\u394d\u394e\u394f\u3950\u3951\u3952\u3953\u3954\u3955\u3956\u3957\u3958\u3959\u395a\u395b\u395c\u395d\u395e\u395f\u3960\u3961\u3962\u3963\u3964\u3965\u3966\u3967\u3968\u3969\u396a\u396b\u396c\u396d\u396e\u396f\u3970\u3971\u3972\u3973\u3974\u3975\u3976\u3977\u3978\u3979\u397a\u397b\u397c\u397d\u397e\u397f\u3980\u3981\u3982\u3983\u3984\u3985\u3986\u3987\u3988\u3989\u398a\u398b\u398c\u398d\u398e\u398f\u3990\u3991\u3992\u3993\u3994\u3995\u3996\u3997\u3998\u3999\u399a\u399b\u399c\u399d\u399e\u399f\u39a0\u39a1\u39a2\u39a3\u39a4\u39a5\u39a6\u39a7\u39a8\u39a9\u39aa\u39ab\u39ac\u39ad\u39ae\u39af\u39b0\u39b1\u39b2\u39b3\u39b4\u39b5\u39b6\u39b7\u39b8\u39b9\u39ba\u39bb\u39bc\u39bd\u39be\u39bf\u39c0\u39c1\u39c2\u39c3\u39c4\u39c5\u39c6\u39c7\u39c8\u39c9\u39ca\u39cb\u39cc\u39cd\u39ce\u39cf\u39d0\u39d1\u39d2\u39d3\u39d4\u39d5\u39d6\u39d7\u39d8\u39d9\u39da\u39db\u39dc\u39dd\u39de\u39df\u39e0\u39e1\u39e2\u39e3\u39e4\u39e5\u39e6\u39e7\u39e8\u39e9\u39ea\u39eb\u39ec\u39ed\u39ee\u39ef\u39f0\u39f1\u39f2\u39f3\u39f4\u39f5\u39f6\u39f7\u39f8\u39f9\u39fa\u39fb\u39fc\u39fd\u39fe\u39ff\u3a00\u3a01\u3a02\u3a03\u3a04\u3a05\u3a06\u3a07\u3a08\u3a09\u3a0a\u3a0b\u3a0c\u3a0d\u3a0e\u3a0f\u3a10\u3a11\u3a12\u3a13\u3a14\u3a15\u3a16\u3a17\u3a18\u3a19\u3a1a\u3a1b\u3a1c\u3a1d\u3a1e\u3a1f\u3a20\u3a21\u3a22\u3a23\u3a24\u3a25\u3a26\u3a27\u3a28\u3a29\u3a2a\u3a2b\u3a2c\u3a2d\u3a2e\u3a2f\u3a30\u3a31\u3a32\u3a33\u3a34\u3a35\u3a36\u3a37\u3a38\u3a39\u3a3a\u3a3b\u3a3c\u3a3d\u3a3e\u3a3f\u3a40\u3a41\u3a42\u3a43\u3a44\u3a45\u3a46\u3a47\u3a48\u3a49\u3a4a\u3a4b\u3a4c\u3a4d\u3a4e\u3a4f\u3a50\u3a51\u3a52\u3a53\u3a54\u3a55\u3a56\u3a57\u3a58\u3a59\u3a5a\u3a5b\u3a5c\u3a5d\u3a5e\u3a5f\u3a60\u3a61\u3a62\u3a63\u3a64\u3a65\u3a66\u3a67\u3a68\u3a69\u3a6a\u3a6b\u3a6c\u3a6d\u3a6e\u3a6f\u3a70\u3a71\u3a72\u3a73\u3a74\u3a75\u3a76\u3a77\u3a78\u3a79\u3a7a\u3a7b\u3a7c\u3a7d\u3a7e\u3a7f\u3a80\u3a81\u3a82\u3a83\u3a84\u3a85\u3a86\u3a87\u3a88\u3a89\u3a8a\u3a8b\u3a8c\u3a8d\u3a8e\u3a8f\u3a90\u3a91\u3a92\u3a93\u3a94\u3a95\u3a96\u3a97\u3a98\u3a99\u3a9a\u3a9b\u3a9c\u3a9d\u3a9e\u3a9f\u3aa0\u3aa1\u3aa2\u3aa3\u3aa4\u3aa5\u3aa6\u3aa7\u3aa8\u3aa9\u3aaa\u3aab\u3aac\u3aad\u3aae\u3aaf\u3ab0\u3ab1\u3ab2\u3ab3\u3ab4\u3ab5\u3ab6\u3ab7\u3ab8\u3ab9\u3aba\u3abb\u3abc\u3abd\u3abe\u3abf\u3ac0\u3ac1\u3ac2\u3ac3\u3ac4\u3ac5\u3ac6\u3ac7\u3ac8\u3ac9\u3aca\u3acb\u3acc\u3acd\u3ace\u3acf\u3ad0\u3ad1\u3ad2\u3ad3\u3ad4\u3ad5\u3ad6\u3ad7\u3ad8\u3ad9\u3ada\u3adb\u3adc\u3add\u3ade\u3adf\u3ae0\u3ae1\u3ae2\u3ae3\u3ae4\u3ae5\u3ae6\u3ae7\u3ae8\u3ae9\u3aea\u3aeb\u3aec\u3aed\u3aee\u3aef\u3af0\u3af1\u3af2\u3af3\u3af4\u3af5\u3af6\u3af7\u3af8\u3af9\u3afa\u3afb\u3afc\u3afd\u3afe\u3aff\u3b00\u3b01\u3b02\u3b03\u3b04\u3b05\u3b06\u3b07\u3b08\u3b09\u3b0a\u3b0b\u3b0c\u3b0d\u3b0e\u3b0f\u3b10\u3b11\u3b12\u3b13\u3b14\u3b15\u3b16\u3b17\u3b18\u3b19\u3b1a\u3b1b\u3b1c\u3b1d\u3b1e\u3b1f\u3b20\u3b21\u3b22\u3b23\u3b24\u3b25\u3b26\u3b27\u3b28\u3b29\u3b2a\u3b2b\u3b2c\u3b2d\u3b2e\u3b2f\u3b30\u3b31\u3b32\u3b33\u3b34\u3b35\u3b36\u3b37\u3b38\u3b39\u3b3a\u3b3b\u3b3c\u3b3d\u3b3e\u3b3f\u3b40\u3b41\u3b42\u3b43\u3b44\u3b45\u3b46\u3b47\u3b48\u3b49\u3b4a\u3b4b\u3b4c\u3b4d\u3b4e\u3b4f\u3b50\u3b51\u3b52\u3b53\u3b54\u3b55\u3b56\u3b57\u3b58\u3b59\u3b5a\u3b5b\u3b5c\u3b5d\u3b5e\u3b5f\u3b60\u3b61\u3b62\u3b63\u3b64\u3b65\u3b66\u3b67\u3b68\u3b69\u3b6a\u3b6b\u3b6c\u3b6d\u3b6e\u3b6f\u3b70\u3b71\u3b72\u3b73\u3b74\u3b75\u3b76\u3b77\u3b78\u3b79\u3b7a\u3b7b\u3b7c\u3b7d\u3b7e\u3b7f\u3b80\u3b81\u3b82\u3b83\u3b84\u3b85\u3b86\u3b87\u3b88\u3b89\u3b8a\u3b8b\u3b8c\u3b8d\u3b8e\u3b8f\u3b90\u3b91\u3b92\u3b93\u3b94\u3b95\u3b96\u3b97\u3b98\u3b99\u3b9a\u3b9b\u3b9c\u3b9d\u3b9e\u3b9f\u3ba0\u3ba1\u3ba2\u3ba3\u3ba4\u3ba5\u3ba6\u3ba7\u3ba8\u3ba9\u3baa\u3bab\u3bac\u3bad\u3bae\u3baf\u3bb0\u3bb1\u3bb2\u3bb3\u3bb4\u3bb5\u3bb6\u3bb7\u3bb8\u3bb9\u3bba\u3bbb\u3bbc\u3bbd\u3bbe\u3bbf\u3bc0\u3bc1\u3bc2\u3bc3\u3bc4\u3bc5\u3bc6\u3bc7\u3bc8\u3bc9\u3bca\u3bcb\u3bcc\u3bcd\u3bce\u3bcf\u3bd0\u3bd1\u3bd2\u3bd3\u3bd4\u3bd5\u3bd6\u3bd7\u3bd8\u3bd9\u3bda\u3bdb\u3bdc\u3bdd\u3bde\u3bdf\u3be0\u3be1\u3be2\u3be3\u3be4\u3be5\u3be6\u3be7\u3be8\u3be9\u3bea\u3beb\u3bec\u3bed\u3bee\u3bef\u3bf0\u3bf1\u3bf2\u3bf3\u3bf4\u3bf5\u3bf6\u3bf7\u3bf8\u3bf9\u3bfa\u3bfb\u3bfc\u3bfd\u3bfe\u3bff\u3c00\u3c01\u3c02\u3c03\u3c04\u3c05\u3c06\u3c07\u3c08\u3c09\u3c0a\u3c0b\u3c0c\u3c0d\u3c0e\u3c0f\u3c10\u3c11\u3c12\u3c13\u3c14\u3c15\u3c16\u3c17\u3c18\u3c19\u3c1a\u3c1b\u3c1c\u3c1d\u3c1e\u3c1f\u3c20\u3c21\u3c22\u3c23\u3c24\u3c25\u3c26\u3c27\u3c28\u3c29\u3c2a\u3c2b\u3c2c\u3c2d\u3c2e\u3c2f\u3c30\u3c31\u3c32\u3c33\u3c34\u3c35\u3c36\u3c37\u3c38\u3c39\u3c3a\u3c3b\u3c3c\u3c3d\u3c3e\u3c3f\u3c40\u3c41\u3c42\u3c43\u3c44\u3c45\u3c46\u3c47\u3c48\u3c49\u3c4a\u3c4b\u3c4c\u3c4d\u3c4e\u3c4f\u3c50\u3c51\u3c52\u3c53\u3c54\u3c55\u3c56\u3c57\u3c58\u3c59\u3c5a\u3c5b\u3c5c\u3c5d\u3c5e\u3c5f\u3c60\u3c61\u3c62\u3c63\u3c64\u3c65\u3c66\u3c67\u3c68\u3c69\u3c6a\u3c6b\u3c6c\u3c6d\u3c6e\u3c6f\u3c70\u3c71\u3c72\u3c73\u3c74\u3c75\u3c76\u3c77\u3c78\u3c79\u3c7a\u3c7b\u3c7c\u3c7d\u3c7e\u3c7f\u3c80\u3c81\u3c82\u3c83\u3c84\u3c85\u3c86\u3c87\u3c88\u3c89\u3c8a\u3c8b\u3c8c\u3c8d\u3c8e\u3c8f\u3c90\u3c91\u3c92\u3c93\u3c94\u3c95\u3c96\u3c97\u3c98\u3c99\u3c9a\u3c9b\u3c9c\u3c9d\u3c9e\u3c9f\u3ca0\u3ca1\u3ca2\u3ca3\u3ca4\u3ca5\u3ca6\u3ca7\u3ca8\u3ca9\u3caa\u3cab\u3cac\u3cad\u3cae\u3caf\u3cb0\u3cb1\u3cb2\u3cb3\u3cb4\u3cb5\u3cb6\u3cb7\u3cb8\u3cb9\u3cba\u3cbb\u3cbc\u3cbd\u3cbe\u3cbf\u3cc0\u3cc1\u3cc2\u3cc3\u3cc4\u3cc5\u3cc6\u3cc7\u3cc8\u3cc9\u3cca\u3ccb\u3ccc\u3ccd\u3cce\u3ccf\u3cd0\u3cd1\u3cd2\u3cd3\u3cd4\u3cd5\u3cd6\u3cd7\u3cd8\u3cd9\u3cda\u3cdb\u3cdc\u3cdd\u3cde\u3cdf\u3ce0\u3ce1\u3ce2\u3ce3\u3ce4\u3ce5\u3ce6\u3ce7\u3ce8\u3ce9\u3cea\u3ceb\u3cec\u3ced\u3cee\u3cef\u3cf0\u3cf1\u3cf2\u3cf3\u3cf4\u3cf5\u3cf6\u3cf7\u3cf8\u3cf9\u3cfa\u3cfb\u3cfc\u3cfd\u3cfe\u3cff\u3d00\u3d01\u3d02\u3d03\u3d04\u3d05\u3d06\u3d07\u3d08\u3d09\u3d0a\u3d0b\u3d0c\u3d0d\u3d0e\u3d0f\u3d10\u3d11\u3d12\u3d13\u3d14\u3d15\u3d16\u3d17\u3d18\u3d19\u3d1a\u3d1b\u3d1c\u3d1d\u3d1e\u3d1f\u3d20\u3d21\u3d22\u3d23\u3d24\u3d25\u3d26\u3d27\u3d28\u3d29\u3d2a\u3d2b\u3d2c\u3d2d\u3d2e\u3d2f\u3d30\u3d31\u3d32\u3d33\u3d34\u3d35\u3d36\u3d37\u3d38\u3d39\u3d3a\u3d3b\u3d3c\u3d3d\u3d3e\u3d3f\u3d40\u3d41\u3d42\u3d43\u3d44\u3d45\u3d46\u3d47\u3d48\u3d49\u3d4a\u3d4b\u3d4c\u3d4d\u3d4e\u3d4f\u3d50\u3d51\u3d52\u3d53\u3d54\u3d55\u3d56\u3d57\u3d58\u3d59\u3d5a\u3d5b\u3d5c\u3d5d\u3d5e\u3d5f\u3d60\u3d61\u3d62\u3d63\u3d64\u3d65\u3d66\u3d67\u3d68\u3d69\u3d6a\u3d6b\u3d6c\u3d6d\u3d6e\u3d6f\u3d70\u3d71\u3d72\u3d73\u3d74\u3d75\u3d76\u3d77\u3d78\u3d79\u3d7a\u3d7b\u3d7c\u3d7d\u3d7e\u3d7f\u3d80\u3d81\u3d82\u3d83\u3d84\u3d85\u3d86\u3d87\u3d88\u3d89\u3d8a\u3d8b\u3d8c\u3d8d\u3d8e\u3d8f\u3d90\u3d91\u3d92\u3d93\u3d94\u3d95\u3d96\u3d97\u3d98\u3d99\u3d9a\u3d9b\u3d9c\u3d9d\u3d9e\u3d9f\u3da0\u3da1\u3da2\u3da3\u3da4\u3da5\u3da6\u3da7\u3da8\u3da9\u3daa\u3dab\u3dac\u3dad\u3dae\u3daf\u3db0\u3db1\u3db2\u3db3\u3db4\u3db5\u3db6\u3db7\u3db8\u3db9\u3dba\u3dbb\u3dbc\u3dbd\u3dbe\u3dbf\u3dc0\u3dc1\u3dc2\u3dc3\u3dc4\u3dc5\u3dc6\u3dc7\u3dc8\u3dc9\u3dca\u3dcb\u3dcc\u3dcd\u3dce\u3dcf\u3dd0\u3dd1\u3dd2\u3dd3\u3dd4\u3dd5\u3dd6\u3dd7\u3dd8\u3dd9\u3dda\u3ddb\u3ddc\u3ddd\u3dde\u3ddf\u3de0\u3de1\u3de2\u3de3\u3de4\u3de5\u3de6\u3de7\u3de8\u3de9\u3dea\u3deb\u3dec\u3ded\u3dee\u3def\u3df0\u3df1\u3df2\u3df3\u3df4\u3df5\u3df6\u3df7\u3df8\u3df9\u3dfa\u3dfb\u3dfc\u3dfd\u3dfe\u3dff\u3e00\u3e01\u3e02\u3e03\u3e04\u3e05\u3e06\u3e07\u3e08\u3e09\u3e0a\u3e0b\u3e0c\u3e0d\u3e0e\u3e0f\u3e10\u3e11\u3e12\u3e13\u3e14\u3e15\u3e16\u3e17\u3e18\u3e19\u3e1a\u3e1b\u3e1c\u3e1d\u3e1e\u3e1f\u3e20\u3e21\u3e22\u3e23\u3e24\u3e25\u3e26\u3e27\u3e28\u3e29\u3e2a\u3e2b\u3e2c\u3e2d\u3e2e\u3e2f\u3e30\u3e31\u3e32\u3e33\u3e34\u3e35\u3e36\u3e37\u3e38\u3e39\u3e3a\u3e3b\u3e3c\u3e3d\u3e3e\u3e3f\u3e40\u3e41\u3e42\u3e43\u3e44\u3e45\u3e46\u3e47\u3e48\u3e49\u3e4a\u3e4b\u3e4c\u3e4d\u3e4e\u3e4f\u3e50\u3e51\u3e52\u3e53\u3e54\u3e55\u3e56\u3e57\u3e58\u3e59\u3e5a\u3e5b\u3e5c\u3e5d\u3e5e\u3e5f\u3e60\u3e61\u3e62\u3e63\u3e64\u3e65\u3e66\u3e67\u3e68\u3e69\u3e6a\u3e6b\u3e6c\u3e6d\u3e6e\u3e6f\u3e70\u3e71\u3e72\u3e73\u3e74\u3e75\u3e76\u3e77\u3e78\u3e79\u3e7a\u3e7b\u3e7c\u3e7d\u3e7e\u3e7f\u3e80\u3e81\u3e82\u3e83\u3e84\u3e85\u3e86\u3e87\u3e88\u3e89\u3e8a\u3e8b\u3e8c\u3e8d\u3e8e\u3e8f\u3e90\u3e91\u3e92\u3e93\u3e94\u3e95\u3e96\u3e97\u3e98\u3e99\u3e9a\u3e9b\u3e9c\u3e9d\u3e9e\u3e9f\u3ea0\u3ea1\u3ea2\u3ea3\u3ea4\u3ea5\u3ea6\u3ea7\u3ea8\u3ea9\u3eaa\u3eab\u3eac\u3ead\u3eae\u3eaf\u3eb0\u3eb1\u3eb2\u3eb3\u3eb4\u3eb5\u3eb6\u3eb7\u3eb8\u3eb9\u3eba\u3ebb\u3ebc\u3ebd\u3ebe\u3ebf\u3ec0\u3ec1\u3ec2\u3ec3\u3ec4\u3ec5\u3ec6\u3ec7\u3ec8\u3ec9\u3eca\u3ecb\u3ecc\u3ecd\u3ece\u3ecf\u3ed0\u3ed1\u3ed2\u3ed3\u3ed4\u3ed5\u3ed6\u3ed7\u3ed8\u3ed9\u3eda\u3edb\u3edc\u3edd\u3ede\u3edf\u3ee0\u3ee1\u3ee2\u3ee3\u3ee4\u3ee5\u3ee6\u3ee7\u3ee8\u3ee9\u3eea\u3eeb\u3eec\u3eed\u3eee\u3eef\u3ef0\u3ef1\u3ef2\u3ef3\u3ef4\u3ef5\u3ef6\u3ef7\u3ef8\u3ef9\u3efa\u3efb\u3efc\u3efd\u3efe\u3eff\u3f00\u3f01\u3f02\u3f03\u3f04\u3f05\u3f06\u3f07\u3f08\u3f09\u3f0a\u3f0b\u3f0c\u3f0d\u3f0e\u3f0f\u3f10\u3f11\u3f12\u3f13\u3f14\u3f15\u3f16\u3f17\u3f18\u3f19\u3f1a\u3f1b\u3f1c\u3f1d\u3f1e\u3f1f\u3f20\u3f21\u3f22\u3f23\u3f24\u3f25\u3f26\u3f27\u3f28\u3f29\u3f2a\u3f2b\u3f2c\u3f2d\u3f2e\u3f2f\u3f30\u3f31\u3f32\u3f33\u3f34\u3f35\u3f36\u3f37\u3f38\u3f39\u3f3a\u3f3b\u3f3c\u3f3d\u3f3e\u3f3f\u3f40\u3f41\u3f42\u3f43\u3f44\u3f45\u3f46\u3f47\u3f48\u3f49\u3f4a\u3f4b\u3f4c\u3f4d\u3f4e\u3f4f\u3f50\u3f51\u3f52\u3f53\u3f54\u3f55\u3f56\u3f57\u3f58\u3f59\u3f5a\u3f5b\u3f5c\u3f5d\u3f5e\u3f5f\u3f60\u3f61\u3f62\u3f63\u3f64\u3f65\u3f66\u3f67\u3f68\u3f69\u3f6a\u3f6b\u3f6c\u3f6d\u3f6e\u3f6f\u3f70\u3f71\u3f72\u3f73\u3f74\u3f75\u3f76\u3f77\u3f78\u3f79\u3f7a\u3f7b\u3f7c\u3f7d\u3f7e\u3f7f\u3f80\u3f81\u3f82\u3f83\u3f84\u3f85\u3f86\u3f87\u3f88\u3f89\u3f8a\u3f8b\u3f8c\u3f8d\u3f8e\u3f8f\u3f90\u3f91\u3f92\u3f93\u3f94\u3f95\u3f96\u3f97\u3f98\u3f99\u3f9a\u3f9b\u3f9c\u3f9d\u3f9e\u3f9f\u3fa0\u3fa1\u3fa2\u3fa3\u3fa4\u3fa5\u3fa6\u3fa7\u3fa8\u3fa9\u3faa\u3fab\u3fac\u3fad\u3fae\u3faf\u3fb0\u3fb1\u3fb2\u3fb3\u3fb4\u3fb5\u3fb6\u3fb7\u3fb8\u3fb9\u3fba\u3fbb\u3fbc\u3fbd\u3fbe\u3fbf\u3fc0\u3fc1\u3fc2\u3fc3\u3fc4\u3fc5\u3fc6\u3fc7\u3fc8\u3fc9\u3fca\u3fcb\u3fcc\u3fcd\u3fce\u3fcf\u3fd0\u3fd1\u3fd2\u3fd3\u3fd4\u3fd5\u3fd6\u3fd7\u3fd8\u3fd9\u3fda\u3fdb\u3fdc\u3fdd\u3fde\u3fdf\u3fe0\u3fe1\u3fe2\u3fe3\u3fe4\u3fe5\u3fe6\u3fe7\u3fe8\u3fe9\u3fea\u3feb\u3fec\u3fed\u3fee\u3fef\u3ff0\u3ff1\u3ff2\u3ff3\u3ff4\u3ff5\u3ff6\u3ff7\u3ff8\u3ff9\u3ffa\u3ffb\u3ffc\u3ffd\u3ffe\u3fff\u4000\u4001\u4002\u4003\u4004\u4005\u4006\u4007\u4008\u4009\u400a\u400b\u400c\u400d\u400e\u400f\u4010\u4011\u4012\u4013\u4014\u4015\u4016\u4017\u4018\u4019\u401a\u401b\u401c\u401d\u401e\u401f\u4020\u4021\u4022\u4023\u4024\u4025\u4026\u4027\u4028\u4029\u402a\u402b\u402c\u402d\u402e\u402f\u4030\u4031\u4032\u4033\u4034\u4035\u4036\u4037\u4038\u4039\u403a\u403b\u403c\u403d\u403e\u403f\u4040\u4041\u4042\u4043\u4044\u4045\u4046\u4047\u4048\u4049\u404a\u404b\u404c\u404d\u404e\u404f\u4050\u4051\u4052\u4053\u4054\u4055\u4056\u4057\u4058\u4059\u405a\u405b\u405c\u405d\u405e\u405f\u4060\u4061\u4062\u4063\u4064\u4065\u4066\u4067\u4068\u4069\u406a\u406b\u406c\u406d\u406e\u406f\u4070\u4071\u4072\u4073\u4074\u4075\u4076\u4077\u4078\u4079\u407a\u407b\u407c\u407d\u407e\u407f\u4080\u4081\u4082\u4083\u4084\u4085\u4086\u4087\u4088\u4089\u408a\u408b\u408c\u408d\u408e\u408f\u4090\u4091\u4092\u4093\u4094\u4095\u4096\u4097\u4098\u4099\u409a\u409b\u409c\u409d\u409e\u409f\u40a0\u40a1\u40a2\u40a3\u40a4\u40a5\u40a6\u40a7\u40a8\u40a9\u40aa\u40ab\u40ac\u40ad\u40ae\u40af\u40b0\u40b1\u40b2\u40b3\u40b4\u40b5\u40b6\u40b7\u40b8\u40b9\u40ba\u40bb\u40bc\u40bd\u40be\u40bf\u40c0\u40c1\u40c2\u40c3\u40c4\u40c5\u40c6\u40c7\u40c8\u40c9\u40ca\u40cb\u40cc\u40cd\u40ce\u40cf\u40d0\u40d1\u40d2\u40d3\u40d4\u40d5\u40d6\u40d7\u40d8\u40d9\u40da\u40db\u40dc\u40dd\u40de\u40df\u40e0\u40e1\u40e2\u40e3\u40e4\u40e5\u40e6\u40e7\u40e8\u40e9\u40ea\u40eb\u40ec\u40ed\u40ee\u40ef\u40f0\u40f1\u40f2\u40f3\u40f4\u40f5\u40f6\u40f7\u40f8\u40f9\u40fa\u40fb\u40fc\u40fd\u40fe\u40ff\u4100\u4101\u4102\u4103\u4104\u4105\u4106\u4107\u4108\u4109\u410a\u410b\u410c\u410d\u410e\u410f\u4110\u4111\u4112\u4113\u4114\u4115\u4116\u4117\u4118\u4119\u411a\u411b\u411c\u411d\u411e\u411f\u4120\u4121\u4122\u4123\u4124\u4125\u4126\u4127\u4128\u4129\u412a\u412b\u412c\u412d\u412e\u412f\u4130\u4131\u4132\u4133\u4134\u4135\u4136\u4137\u4138\u4139\u413a\u413b\u413c\u413d\u413e\u413f\u4140\u4141\u4142\u4143\u4144\u4145\u4146\u4147\u4148\u4149\u414a\u414b\u414c\u414d\u414e\u414f\u4150\u4151\u4152\u4153\u4154\u4155\u4156\u4157\u4158\u4159\u415a\u415b\u415c\u415d\u415e\u415f\u4160\u4161\u4162\u4163\u4164\u4165\u4166\u4167\u4168\u4169\u416a\u416b\u416c\u416d\u416e\u416f\u4170\u4171\u4172\u4173\u4174\u4175\u4176\u4177\u4178\u4179\u417a\u417b\u417c\u417d\u417e\u417f\u4180\u4181\u4182\u4183\u4184\u4185\u4186\u4187\u4188\u4189\u418a\u418b\u418c\u418d\u418e\u418f\u4190\u4191\u4192\u4193\u4194\u4195\u4196\u4197\u4198\u4199\u419a\u419b\u419c\u419d\u419e\u419f\u41a0\u41a1\u41a2\u41a3\u41a4\u41a5\u41a6\u41a7\u41a8\u41a9\u41aa\u41ab\u41ac\u41ad\u41ae\u41af\u41b0\u41b1\u41b2\u41b3\u41b4\u41b5\u41b6\u41b7\u41b8\u41b9\u41ba\u41bb\u41bc\u41bd\u41be\u41bf\u41c0\u41c1\u41c2\u41c3\u41c4\u41c5\u41c6\u41c7\u41c8\u41c9\u41ca\u41cb\u41cc\u41cd\u41ce\u41cf\u41d0\u41d1\u41d2\u41d3\u41d4\u41d5\u41d6\u41d7\u41d8\u41d9\u41da\u41db\u41dc\u41dd\u41de\u41df\u41e0\u41e1\u41e2\u41e3\u41e4\u41e5\u41e6\u41e7\u41e8\u41e9\u41ea\u41eb\u41ec\u41ed\u41ee\u41ef\u41f0\u41f1\u41f2\u41f3\u41f4\u41f5\u41f6\u41f7\u41f8\u41f9\u41fa\u41fb\u41fc\u41fd\u41fe\u41ff\u4200\u4201\u4202\u4203\u4204\u4205\u4206\u4207\u4208\u4209\u420a\u420b\u420c\u420d\u420e\u420f\u4210\u4211\u4212\u4213\u4214\u4215\u4216\u4217\u4218\u4219\u421a\u421b\u421c\u421d\u421e\u421f\u4220\u4221\u4222\u4223\u4224\u4225\u4226\u4227\u4228\u4229\u422a\u422b\u422c\u422d\u422e\u422f\u4230\u4231\u4232\u4233\u4234\u4235\u4236\u4237\u4238\u4239\u423a\u423b\u423c\u423d\u423e\u423f\u4240\u4241\u4242\u4243\u4244\u4245\u4246\u4247\u4248\u4249\u424a\u424b\u424c\u424d\u424e\u424f\u4250\u4251\u4252\u4253\u4254\u4255\u4256\u4257\u4258\u4259\u425a\u425b\u425c\u425d\u425e\u425f\u4260\u4261\u4262\u4263\u4264\u4265\u4266\u4267\u4268\u4269\u426a\u426b\u426c\u426d\u426e\u426f\u4270\u4271\u4272\u4273\u4274\u4275\u4276\u4277\u4278\u4279\u427a\u427b\u427c\u427d\u427e\u427f\u4280\u4281\u4282\u4283\u4284\u4285\u4286\u4287\u4288\u4289\u428a\u428b\u428c\u428d\u428e\u428f\u4290\u4291\u4292\u4293\u4294\u4295\u4296\u4297\u4298\u4299\u429a\u429b\u429c\u429d\u429e\u429f\u42a0\u42a1\u42a2\u42a3\u42a4\u42a5\u42a6\u42a7\u42a8\u42a9\u42aa\u42ab\u42ac\u42ad\u42ae\u42af\u42b0\u42b1\u42b2\u42b3\u42b4\u42b5\u42b6\u42b7\u42b8\u42b9\u42ba\u42bb\u42bc\u42bd\u42be\u42bf\u42c0\u42c1\u42c2\u42c3\u42c4\u42c5\u42c6\u42c7\u42c8\u42c9\u42ca\u42cb\u42cc\u42cd\u42ce\u42cf\u42d0\u42d1\u42d2\u42d3\u42d4\u42d5\u42d6\u42d7\u42d8\u42d9\u42da\u42db\u42dc\u42dd\u42de\u42df\u42e0\u42e1\u42e2\u42e3\u42e4\u42e5\u42e6\u42e7\u42e8\u42e9\u42ea\u42eb\u42ec\u42ed\u42ee\u42ef\u42f0\u42f1\u42f2\u42f3\u42f4\u42f5\u42f6\u42f7\u42f8\u42f9\u42fa\u42fb\u42fc\u42fd\u42fe\u42ff\u4300\u4301\u4302\u4303\u4304\u4305\u4306\u4307\u4308\u4309\u430a\u430b\u430c\u430d\u430e\u430f\u4310\u4311\u4312\u4313\u4314\u4315\u4316\u4317\u4318\u4319\u431a\u431b\u431c\u431d\u431e\u431f\u4320\u4321\u4322\u4323\u4324\u4325\u4326\u4327\u4328\u4329\u432a\u432b\u432c\u432d\u432e\u432f\u4330\u4331\u4332\u4333\u4334\u4335\u4336\u4337\u4338\u4339\u433a\u433b\u433c\u433d\u433e\u433f\u4340\u4341\u4342\u4343\u4344\u4345\u4346\u4347\u4348\u4349\u434a\u434b\u434c\u434d\u434e\u434f\u4350\u4351\u4352\u4353\u4354\u4355\u4356\u4357\u4358\u4359\u435a\u435b\u435c\u435d\u435e\u435f\u4360\u4361\u4362\u4363\u4364\u4365\u4366\u4367\u4368\u4369\u436a\u436b\u436c\u436d\u436e\u436f\u4370\u4371\u4372\u4373\u4374\u4375\u4376\u4377\u4378\u4379\u437a\u437b\u437c\u437d\u437e\u437f\u4380\u4381\u4382\u4383\u4384\u4385\u4386\u4387\u4388\u4389\u438a\u438b\u438c\u438d\u438e\u438f\u4390\u4391\u4392\u4393\u4394\u4395\u4396\u4397\u4398\u4399\u439a\u439b\u439c\u439d\u439e\u439f\u43a0\u43a1\u43a2\u43a3\u43a4\u43a5\u43a6\u43a7\u43a8\u43a9\u43aa\u43ab\u43ac\u43ad\u43ae\u43af\u43b0\u43b1\u43b2\u43b3\u43b4\u43b5\u43b6\u43b7\u43b8\u43b9\u43ba\u43bb\u43bc\u43bd\u43be\u43bf\u43c0\u43c1\u43c2\u43c3\u43c4\u43c5\u43c6\u43c7\u43c8\u43c9\u43ca\u43cb\u43cc\u43cd\u43ce\u43cf\u43d0\u43d1\u43d2\u43d3\u43d4\u43d5\u43d6\u43d7\u43d8\u43d9\u43da\u43db\u43dc\u43dd\u43de\u43df\u43e0\u43e1\u43e2\u43e3\u43e4\u43e5\u43e6\u43e7\u43e8\u43e9\u43ea\u43eb\u43ec\u43ed\u43ee\u43ef\u43f0\u43f1\u43f2\u43f3\u43f4\u43f5\u43f6\u43f7\u43f8\u43f9\u43fa\u43fb\u43fc\u43fd\u43fe\u43ff\u4400\u4401\u4402\u4403\u4404\u4405\u4406\u4407\u4408\u4409\u440a\u440b\u440c\u440d\u440e\u440f\u4410\u4411\u4412\u4413\u4414\u4415\u4416\u4417\u4418\u4419\u441a\u441b\u441c\u441d\u441e\u441f\u4420\u4421\u4422\u4423\u4424\u4425\u4426\u4427\u4428\u4429\u442a\u442b\u442c\u442d\u442e\u442f\u4430\u4431\u4432\u4433\u4434\u4435\u4436\u4437\u4438\u4439\u443a\u443b\u443c\u443d\u443e\u443f\u4440\u4441\u4442\u4443\u4444\u4445\u4446\u4447\u4448\u4449\u444a\u444b\u444c\u444d\u444e\u444f\u4450\u4451\u4452\u4453\u4454\u4455\u4456\u4457\u4458\u4459\u445a\u445b\u445c\u445d\u445e\u445f\u4460\u4461\u4462\u4463\u4464\u4465\u4466\u4467\u4468\u4469\u446a\u446b\u446c\u446d\u446e\u446f\u4470\u4471\u4472\u4473\u4474\u4475\u4476\u4477\u4478\u4479\u447a\u447b\u447c\u447d\u447e\u447f\u4480\u4481\u4482\u4483\u4484\u4485\u4486\u4487\u4488\u4489\u448a\u448b\u448c\u448d\u448e\u448f\u4490\u4491\u4492\u4493\u4494\u4495\u4496\u4497\u4498\u4499\u449a\u449b\u449c\u449d\u449e\u449f\u44a0\u44a1\u44a2\u44a3\u44a4\u44a5\u44a6\u44a7\u44a8\u44a9\u44aa\u44ab\u44ac\u44ad\u44ae\u44af\u44b0\u44b1\u44b2\u44b3\u44b4\u44b5\u44b6\u44b7\u44b8\u44b9\u44ba\u44bb\u44bc\u44bd\u44be\u44bf\u44c0\u44c1\u44c2\u44c3\u44c4\u44c5\u44c6\u44c7\u44c8\u44c9\u44ca\u44cb\u44cc\u44cd\u44ce\u44cf\u44d0\u44d1\u44d2\u44d3\u44d4\u44d5\u44d6\u44d7\u44d8\u44d9\u44da\u44db\u44dc\u44dd\u44de\u44df\u44e0\u44e1\u44e2\u44e3\u44e4\u44e5\u44e6\u44e7\u44e8\u44e9\u44ea\u44eb\u44ec\u44ed\u44ee\u44ef\u44f0\u44f1\u44f2\u44f3\u44f4\u44f5\u44f6\u44f7\u44f8\u44f9\u44fa\u44fb\u44fc\u44fd\u44fe\u44ff\u4500\u4501\u4502\u4503\u4504\u4505\u4506\u4507\u4508\u4509\u450a\u450b\u450c\u450d\u450e\u450f\u4510\u4511\u4512\u4513\u4514\u4515\u4516\u4517\u4518\u4519\u451a\u451b\u451c\u451d\u451e\u451f\u4520\u4521\u4522\u4523\u4524\u4525\u4526\u4527\u4528\u4529\u452a\u452b\u452c\u452d\u452e\u452f\u4530\u4531\u4532\u4533\u4534\u4535\u4536\u4537\u4538\u4539\u453a\u453b\u453c\u453d\u453e\u453f\u4540\u4541\u4542\u4543\u4544\u4545\u4546\u4547\u4548\u4549\u454a\u454b\u454c\u454d\u454e\u454f\u4550\u4551\u4552\u4553\u4554\u4555\u4556\u4557\u4558\u4559\u455a\u455b\u455c\u455d\u455e\u455f\u4560\u4561\u4562\u4563\u4564\u4565\u4566\u4567\u4568\u4569\u456a\u456b\u456c\u456d\u456e\u456f\u4570\u4571\u4572\u4573\u4574\u4575\u4576\u4577\u4578\u4579\u457a\u457b\u457c\u457d\u457e\u457f\u4580\u4581\u4582\u4583\u4584\u4585\u4586\u4587\u4588\u4589\u458a\u458b\u458c\u458d\u458e\u458f\u4590\u4591\u4592\u4593\u4594\u4595\u4596\u4597\u4598\u4599\u459a\u459b\u459c\u459d\u459e\u459f\u45a0\u45a1\u45a2\u45a3\u45a4\u45a5\u45a6\u45a7\u45a8\u45a9\u45aa\u45ab\u45ac\u45ad\u45ae\u45af\u45b0\u45b1\u45b2\u45b3\u45b4\u45b5\u45b6\u45b7\u45b8\u45b9\u45ba\u45bb\u45bc\u45bd\u45be\u45bf\u45c0\u45c1\u45c2\u45c3\u45c4\u45c5\u45c6\u45c7\u45c8\u45c9\u45ca\u45cb\u45cc\u45cd\u45ce\u45cf\u45d0\u45d1\u45d2\u45d3\u45d4\u45d5\u45d6\u45d7\u45d8\u45d9\u45da\u45db\u45dc\u45dd\u45de\u45df\u45e0\u45e1\u45e2\u45e3\u45e4\u45e5\u45e6\u45e7\u45e8\u45e9\u45ea\u45eb\u45ec\u45ed\u45ee\u45ef\u45f0\u45f1\u45f2\u45f3\u45f4\u45f5\u45f6\u45f7\u45f8\u45f9\u45fa\u45fb\u45fc\u45fd\u45fe\u45ff\u4600\u4601\u4602\u4603\u4604\u4605\u4606\u4607\u4608\u4609\u460a\u460b\u460c\u460d\u460e\u460f\u4610\u4611\u4612\u4613\u4614\u4615\u4616\u4617\u4618\u4619\u461a\u461b\u461c\u461d\u461e\u461f\u4620\u4621\u4622\u4623\u4624\u4625\u4626\u4627\u4628\u4629\u462a\u462b\u462c\u462d\u462e\u462f\u4630\u4631\u4632\u4633\u4634\u4635\u4636\u4637\u4638\u4639\u463a\u463b\u463c\u463d\u463e\u463f\u4640\u4641\u4642\u4643\u4644\u4645\u4646\u4647\u4648\u4649\u464a\u464b\u464c\u464d\u464e\u464f\u4650\u4651\u4652\u4653\u4654\u4655\u4656\u4657\u4658\u4659\u465a\u465b\u465c\u465d\u465e\u465f\u4660\u4661\u4662\u4663\u4664\u4665\u4666\u4667\u4668\u4669\u466a\u466b\u466c\u466d\u466e\u466f\u4670\u4671\u4672\u4673\u4674\u4675\u4676\u4677\u4678\u4679\u467a\u467b\u467c\u467d\u467e\u467f\u4680\u4681\u4682\u4683\u4684\u4685\u4686\u4687\u4688\u4689\u468a\u468b\u468c\u468d\u468e\u468f\u4690\u4691\u4692\u4693\u4694\u4695\u4696\u4697\u4698\u4699\u469a\u469b\u469c\u469d\u469e\u469f\u46a0\u46a1\u46a2\u46a3\u46a4\u46a5\u46a6\u46a7\u46a8\u46a9\u46aa\u46ab\u46ac\u46ad\u46ae\u46af\u46b0\u46b1\u46b2\u46b3\u46b4\u46b5\u46b6\u46b7\u46b8\u46b9\u46ba\u46bb\u46bc\u46bd\u46be\u46bf\u46c0\u46c1\u46c2\u46c3\u46c4\u46c5\u46c6\u46c7\u46c8\u46c9\u46ca\u46cb\u46cc\u46cd\u46ce\u46cf\u46d0\u46d1\u46d2\u46d3\u46d4\u46d5\u46d6\u46d7\u46d8\u46d9\u46da\u46db\u46dc\u46dd\u46de\u46df\u46e0\u46e1\u46e2\u46e3\u46e4\u46e5\u46e6\u46e7\u46e8\u46e9\u46ea\u46eb\u46ec\u46ed\u46ee\u46ef\u46f0\u46f1\u46f2\u46f3\u46f4\u46f5\u46f6\u46f7\u46f8\u46f9\u46fa\u46fb\u46fc\u46fd\u46fe\u46ff\u4700\u4701\u4702\u4703\u4704\u4705\u4706\u4707\u4708\u4709\u470a\u470b\u470c\u470d\u470e\u470f\u4710\u4711\u4712\u4713\u4714\u4715\u4716\u4717\u4718\u4719\u471a\u471b\u471c\u471d\u471e\u471f\u4720\u4721\u4722\u4723\u4724\u4725\u4726\u4727\u4728\u4729\u472a\u472b\u472c\u472d\u472e\u472f\u4730\u4731\u4732\u4733\u4734\u4735\u4736\u4737\u4738\u4739\u473a\u473b\u473c\u473d\u473e\u473f\u4740\u4741\u4742\u4743\u4744\u4745\u4746\u4747\u4748\u4749\u474a\u474b\u474c\u474d\u474e\u474f\u4750\u4751\u4752\u4753\u4754\u4755\u4756\u4757\u4758\u4759\u475a\u475b\u475c\u475d\u475e\u475f\u4760\u4761\u4762\u4763\u4764\u4765\u4766\u4767\u4768\u4769\u476a\u476b\u476c\u476d\u476e\u476f\u4770\u4771\u4772\u4773\u4774\u4775\u4776\u4777\u4778\u4779\u477a\u477b\u477c\u477d\u477e\u477f\u4780\u4781\u4782\u4783\u4784\u4785\u4786\u4787\u4788\u4789\u478a\u478b\u478c\u478d\u478e\u478f\u4790\u4791\u4792\u4793\u4794\u4795\u4796\u4797\u4798\u4799\u479a\u479b\u479c\u479d\u479e\u479f\u47a0\u47a1\u47a2\u47a3\u47a4\u47a5\u47a6\u47a7\u47a8\u47a9\u47aa\u47ab\u47ac\u47ad\u47ae\u47af\u47b0\u47b1\u47b2\u47b3\u47b4\u47b5\u47b6\u47b7\u47b8\u47b9\u47ba\u47bb\u47bc\u47bd\u47be\u47bf\u47c0\u47c1\u47c2\u47c3\u47c4\u47c5\u47c6\u47c7\u47c8\u47c9\u47ca\u47cb\u47cc\u47cd\u47ce\u47cf\u47d0\u47d1\u47d2\u47d3\u47d4\u47d5\u47d6\u47d7\u47d8\u47d9\u47da\u47db\u47dc\u47dd\u47de\u47df\u47e0\u47e1\u47e2\u47e3\u47e4\u47e5\u47e6\u47e7\u47e8\u47e9\u47ea\u47eb\u47ec\u47ed\u47ee\u47ef\u47f0\u47f1\u47f2\u47f3\u47f4\u47f5\u47f6\u47f7\u47f8\u47f9\u47fa\u47fb\u47fc\u47fd\u47fe\u47ff\u4800\u4801\u4802\u4803\u4804\u4805\u4806\u4807\u4808\u4809\u480a\u480b\u480c\u480d\u480e\u480f\u4810\u4811\u4812\u4813\u4814\u4815\u4816\u4817\u4818\u4819\u481a\u481b\u481c\u481d\u481e\u481f\u4820\u4821\u4822\u4823\u4824\u4825\u4826\u4827\u4828\u4829\u482a\u482b\u482c\u482d\u482e\u482f\u4830\u4831\u4832\u4833\u4834\u4835\u4836\u4837\u4838\u4839\u483a\u483b\u483c\u483d\u483e\u483f\u4840\u4841\u4842\u4843\u4844\u4845\u4846\u4847\u4848\u4849\u484a\u484b\u484c\u484d\u484e\u484f\u4850\u4851\u4852\u4853\u4854\u4855\u4856\u4857\u4858\u4859\u485a\u485b\u485c\u485d\u485e\u485f\u4860\u4861\u4862\u4863\u4864\u4865\u4866\u4867\u4868\u4869\u486a\u486b\u486c\u486d\u486e\u486f\u4870\u4871\u4872\u4873\u4874\u4875\u4876\u4877\u4878\u4879\u487a\u487b\u487c\u487d\u487e\u487f\u4880\u4881\u4882\u4883\u4884\u4885\u4886\u4887\u4888\u4889\u488a\u488b\u488c\u488d\u488e\u488f\u4890\u4891\u4892\u4893\u4894\u4895\u4896\u4897\u4898\u4899\u489a\u489b\u489c\u489d\u489e\u489f\u48a0\u48a1\u48a2\u48a3\u48a4\u48a5\u48a6\u48a7\u48a8\u48a9\u48aa\u48ab\u48ac\u48ad\u48ae\u48af\u48b0\u48b1\u48b2\u48b3\u48b4\u48b5\u48b6\u48b7\u48b8\u48b9\u48ba\u48bb\u48bc\u48bd\u48be\u48bf\u48c0\u48c1\u48c2\u48c3\u48c4\u48c5\u48c6\u48c7\u48c8\u48c9\u48ca\u48cb\u48cc\u48cd\u48ce\u48cf\u48d0\u48d1\u48d2\u48d3\u48d4\u48d5\u48d6\u48d7\u48d8\u48d9\u48da\u48db\u48dc\u48dd\u48de\u48df\u48e0\u48e1\u48e2\u48e3\u48e4\u48e5\u48e6\u48e7\u48e8\u48e9\u48ea\u48eb\u48ec\u48ed\u48ee\u48ef\u48f0\u48f1\u48f2\u48f3\u48f4\u48f5\u48f6\u48f7\u48f8\u48f9\u48fa\u48fb\u48fc\u48fd\u48fe\u48ff\u4900\u4901\u4902\u4903\u4904\u4905\u4906\u4907\u4908\u4909\u490a\u490b\u490c\u490d\u490e\u490f\u4910\u4911\u4912\u4913\u4914\u4915\u4916\u4917\u4918\u4919\u491a\u491b\u491c\u491d\u491e\u491f\u4920\u4921\u4922\u4923\u4924\u4925\u4926\u4927\u4928\u4929\u492a\u492b\u492c\u492d\u492e\u492f\u4930\u4931\u4932\u4933\u4934\u4935\u4936\u4937\u4938\u4939\u493a\u493b\u493c\u493d\u493e\u493f\u4940\u4941\u4942\u4943\u4944\u4945\u4946\u4947\u4948\u4949\u494a\u494b\u494c\u494d\u494e\u494f\u4950\u4951\u4952\u4953\u4954\u4955\u4956\u4957\u4958\u4959\u495a\u495b\u495c\u495d\u495e\u495f\u4960\u4961\u4962\u4963\u4964\u4965\u4966\u4967\u4968\u4969\u496a\u496b\u496c\u496d\u496e\u496f\u4970\u4971\u4972\u4973\u4974\u4975\u4976\u4977\u4978\u4979\u497a\u497b\u497c\u497d\u497e\u497f\u4980\u4981\u4982\u4983\u4984\u4985\u4986\u4987\u4988\u4989\u498a\u498b\u498c\u498d\u498e\u498f\u4990\u4991\u4992\u4993\u4994\u4995\u4996\u4997\u4998\u4999\u499a\u499b\u499c\u499d\u499e\u499f\u49a0\u49a1\u49a2\u49a3\u49a4\u49a5\u49a6\u49a7\u49a8\u49a9\u49aa\u49ab\u49ac\u49ad\u49ae\u49af\u49b0\u49b1\u49b2\u49b3\u49b4\u49b5\u49b6\u49b7\u49b8\u49b9\u49ba\u49bb\u49bc\u49bd\u49be\u49bf\u49c0\u49c1\u49c2\u49c3\u49c4\u49c5\u49c6\u49c7\u49c8\u49c9\u49ca\u49cb\u49cc\u49cd\u49ce\u49cf\u49d0\u49d1\u49d2\u49d3\u49d4\u49d5\u49d6\u49d7\u49d8\u49d9\u49da\u49db\u49dc\u49dd\u49de\u49df\u49e0\u49e1\u49e2\u49e3\u49e4\u49e5\u49e6\u49e7\u49e8\u49e9\u49ea\u49eb\u49ec\u49ed\u49ee\u49ef\u49f0\u49f1\u49f2\u49f3\u49f4\u49f5\u49f6\u49f7\u49f8\u49f9\u49fa\u49fb\u49fc\u49fd\u49fe\u49ff\u4a00\u4a01\u4a02\u4a03\u4a04\u4a05\u4a06\u4a07\u4a08\u4a09\u4a0a\u4a0b\u4a0c\u4a0d\u4a0e\u4a0f\u4a10\u4a11\u4a12\u4a13\u4a14\u4a15\u4a16\u4a17\u4a18\u4a19\u4a1a\u4a1b\u4a1c\u4a1d\u4a1e\u4a1f\u4a20\u4a21\u4a22\u4a23\u4a24\u4a25\u4a26\u4a27\u4a28\u4a29\u4a2a\u4a2b\u4a2c\u4a2d\u4a2e\u4a2f\u4a30\u4a31\u4a32\u4a33\u4a34\u4a35\u4a36\u4a37\u4a38\u4a39\u4a3a\u4a3b\u4a3c\u4a3d\u4a3e\u4a3f\u4a40\u4a41\u4a42\u4a43\u4a44\u4a45\u4a46\u4a47\u4a48\u4a49\u4a4a\u4a4b\u4a4c\u4a4d\u4a4e\u4a4f\u4a50\u4a51\u4a52\u4a53\u4a54\u4a55\u4a56\u4a57\u4a58\u4a59\u4a5a\u4a5b\u4a5c\u4a5d\u4a5e\u4a5f\u4a60\u4a61\u4a62\u4a63\u4a64\u4a65\u4a66\u4a67\u4a68\u4a69\u4a6a\u4a6b\u4a6c\u4a6d\u4a6e\u4a6f\u4a70\u4a71\u4a72\u4a73\u4a74\u4a75\u4a76\u4a77\u4a78\u4a79\u4a7a\u4a7b\u4a7c\u4a7d\u4a7e\u4a7f\u4a80\u4a81\u4a82\u4a83\u4a84\u4a85\u4a86\u4a87\u4a88\u4a89\u4a8a\u4a8b\u4a8c\u4a8d\u4a8e\u4a8f\u4a90\u4a91\u4a92\u4a93\u4a94\u4a95\u4a96\u4a97\u4a98\u4a99\u4a9a\u4a9b\u4a9c\u4a9d\u4a9e\u4a9f\u4aa0\u4aa1\u4aa2\u4aa3\u4aa4\u4aa5\u4aa6\u4aa7\u4aa8\u4aa9\u4aaa\u4aab\u4aac\u4aad\u4aae\u4aaf\u4ab0\u4ab1\u4ab2\u4ab3\u4ab4\u4ab5\u4ab6\u4ab7\u4ab8\u4ab9\u4aba\u4abb\u4abc\u4abd\u4abe\u4abf\u4ac0\u4ac1\u4ac2\u4ac3\u4ac4\u4ac5\u4ac6\u4ac7\u4ac8\u4ac9\u4aca\u4acb\u4acc\u4acd\u4ace\u4acf\u4ad0\u4ad1\u4ad2\u4ad3\u4ad4\u4ad5\u4ad6\u4ad7\u4ad8\u4ad9\u4ada\u4adb\u4adc\u4add\u4ade\u4adf\u4ae0\u4ae1\u4ae2\u4ae3\u4ae4\u4ae5\u4ae6\u4ae7\u4ae8\u4ae9\u4aea\u4aeb\u4aec\u4aed\u4aee\u4aef\u4af0\u4af1\u4af2\u4af3\u4af4\u4af5\u4af6\u4af7\u4af8\u4af9\u4afa\u4afb\u4afc\u4afd\u4afe\u4aff\u4b00\u4b01\u4b02\u4b03\u4b04\u4b05\u4b06\u4b07\u4b08\u4b09\u4b0a\u4b0b\u4b0c\u4b0d\u4b0e\u4b0f\u4b10\u4b11\u4b12\u4b13\u4b14\u4b15\u4b16\u4b17\u4b18\u4b19\u4b1a\u4b1b\u4b1c\u4b1d\u4b1e\u4b1f\u4b20\u4b21\u4b22\u4b23\u4b24\u4b25\u4b26\u4b27\u4b28\u4b29\u4b2a\u4b2b\u4b2c\u4b2d\u4b2e\u4b2f\u4b30\u4b31\u4b32\u4b33\u4b34\u4b35\u4b36\u4b37\u4b38\u4b39\u4b3a\u4b3b\u4b3c\u4b3d\u4b3e\u4b3f\u4b40\u4b41\u4b42\u4b43\u4b44\u4b45\u4b46\u4b47\u4b48\u4b49\u4b4a\u4b4b\u4b4c\u4b4d\u4b4e\u4b4f\u4b50\u4b51\u4b52\u4b53\u4b54\u4b55\u4b56\u4b57\u4b58\u4b59\u4b5a\u4b5b\u4b5c\u4b5d\u4b5e\u4b5f\u4b60\u4b61\u4b62\u4b63\u4b64\u4b65\u4b66\u4b67\u4b68\u4b69\u4b6a\u4b6b\u4b6c\u4b6d\u4b6e\u4b6f\u4b70\u4b71\u4b72\u4b73\u4b74\u4b75\u4b76\u4b77\u4b78\u4b79\u4b7a\u4b7b\u4b7c\u4b7d\u4b7e\u4b7f\u4b80\u4b81\u4b82\u4b83\u4b84\u4b85\u4b86\u4b87\u4b88\u4b89\u4b8a\u4b8b\u4b8c\u4b8d\u4b8e\u4b8f\u4b90\u4b91\u4b92\u4b93\u4b94\u4b95\u4b96\u4b97\u4b98\u4b99\u4b9a\u4b9b\u4b9c\u4b9d\u4b9e\u4b9f\u4ba0\u4ba1\u4ba2\u4ba3\u4ba4\u4ba5\u4ba6\u4ba7\u4ba8\u4ba9\u4baa\u4bab\u4bac\u4bad\u4bae\u4baf\u4bb0\u4bb1\u4bb2\u4bb3\u4bb4\u4bb5\u4bb6\u4bb7\u4bb8\u4bb9\u4bba\u4bbb\u4bbc\u4bbd\u4bbe\u4bbf\u4bc0\u4bc1\u4bc2\u4bc3\u4bc4\u4bc5\u4bc6\u4bc7\u4bc8\u4bc9\u4bca\u4bcb\u4bcc\u4bcd\u4bce\u4bcf\u4bd0\u4bd1\u4bd2\u4bd3\u4bd4\u4bd5\u4bd6\u4bd7\u4bd8\u4bd9\u4bda\u4bdb\u4bdc\u4bdd\u4bde\u4bdf\u4be0\u4be1\u4be2\u4be3\u4be4\u4be5\u4be6\u4be7\u4be8\u4be9\u4bea\u4beb\u4bec\u4bed\u4bee\u4bef\u4bf0\u4bf1\u4bf2\u4bf3\u4bf4\u4bf5\u4bf6\u4bf7\u4bf8\u4bf9\u4bfa\u4bfb\u4bfc\u4bfd\u4bfe\u4bff\u4c00\u4c01\u4c02\u4c03\u4c04\u4c05\u4c06\u4c07\u4c08\u4c09\u4c0a\u4c0b\u4c0c\u4c0d\u4c0e\u4c0f\u4c10\u4c11\u4c12\u4c13\u4c14\u4c15\u4c16\u4c17\u4c18\u4c19\u4c1a\u4c1b\u4c1c\u4c1d\u4c1e\u4c1f\u4c20\u4c21\u4c22\u4c23\u4c24\u4c25\u4c26\u4c27\u4c28\u4c29\u4c2a\u4c2b\u4c2c\u4c2d\u4c2e\u4c2f\u4c30\u4c31\u4c32\u4c33\u4c34\u4c35\u4c36\u4c37\u4c38\u4c39\u4c3a\u4c3b\u4c3c\u4c3d\u4c3e\u4c3f\u4c40\u4c41\u4c42\u4c43\u4c44\u4c45\u4c46\u4c47\u4c48\u4c49\u4c4a\u4c4b\u4c4c\u4c4d\u4c4e\u4c4f\u4c50\u4c51\u4c52\u4c53\u4c54\u4c55\u4c56\u4c57\u4c58\u4c59\u4c5a\u4c5b\u4c5c\u4c5d\u4c5e\u4c5f\u4c60\u4c61\u4c62\u4c63\u4c64\u4c65\u4c66\u4c67\u4c68\u4c69\u4c6a\u4c6b\u4c6c\u4c6d\u4c6e\u4c6f\u4c70\u4c71\u4c72\u4c73\u4c74\u4c75\u4c76\u4c77\u4c78\u4c79\u4c7a\u4c7b\u4c7c\u4c7d\u4c7e\u4c7f\u4c80\u4c81\u4c82\u4c83\u4c84\u4c85\u4c86\u4c87\u4c88\u4c89\u4c8a\u4c8b\u4c8c\u4c8d\u4c8e\u4c8f\u4c90\u4c91\u4c92\u4c93\u4c94\u4c95\u4c96\u4c97\u4c98\u4c99\u4c9a\u4c9b\u4c9c\u4c9d\u4c9e\u4c9f\u4ca0\u4ca1\u4ca2\u4ca3\u4ca4\u4ca5\u4ca6\u4ca7\u4ca8\u4ca9\u4caa\u4cab\u4cac\u4cad\u4cae\u4caf\u4cb0\u4cb1\u4cb2\u4cb3\u4cb4\u4cb5\u4cb6\u4cb7\u4cb8\u4cb9\u4cba\u4cbb\u4cbc\u4cbd\u4cbe\u4cbf\u4cc0\u4cc1\u4cc2\u4cc3\u4cc4\u4cc5\u4cc6\u4cc7\u4cc8\u4cc9\u4cca\u4ccb\u4ccc\u4ccd\u4cce\u4ccf\u4cd0\u4cd1\u4cd2\u4cd3\u4cd4\u4cd5\u4cd6\u4cd7\u4cd8\u4cd9\u4cda\u4cdb\u4cdc\u4cdd\u4cde\u4cdf\u4ce0\u4ce1\u4ce2\u4ce3\u4ce4\u4ce5\u4ce6\u4ce7\u4ce8\u4ce9\u4cea\u4ceb\u4cec\u4ced\u4cee\u4cef\u4cf0\u4cf1\u4cf2\u4cf3\u4cf4\u4cf5\u4cf6\u4cf7\u4cf8\u4cf9\u4cfa\u4cfb\u4cfc\u4cfd\u4cfe\u4cff\u4d00\u4d01\u4d02\u4d03\u4d04\u4d05\u4d06\u4d07\u4d08\u4d09\u4d0a\u4d0b\u4d0c\u4d0d\u4d0e\u4d0f\u4d10\u4d11\u4d12\u4d13\u4d14\u4d15\u4d16\u4d17\u4d18\u4d19\u4d1a\u4d1b\u4d1c\u4d1d\u4d1e\u4d1f\u4d20\u4d21\u4d22\u4d23\u4d24\u4d25\u4d26\u4d27\u4d28\u4d29\u4d2a\u4d2b\u4d2c\u4d2d\u4d2e\u4d2f\u4d30\u4d31\u4d32\u4d33\u4d34\u4d35\u4d36\u4d37\u4d38\u4d39\u4d3a\u4d3b\u4d3c\u4d3d\u4d3e\u4d3f\u4d40\u4d41\u4d42\u4d43\u4d44\u4d45\u4d46\u4d47\u4d48\u4d49\u4d4a\u4d4b\u4d4c\u4d4d\u4d4e\u4d4f\u4d50\u4d51\u4d52\u4d53\u4d54\u4d55\u4d56\u4d57\u4d58\u4d59\u4d5a\u4d5b\u4d5c\u4d5d\u4d5e\u4d5f\u4d60\u4d61\u4d62\u4d63\u4d64\u4d65\u4d66\u4d67\u4d68\u4d69\u4d6a\u4d6b\u4d6c\u4d6d\u4d6e\u4d6f\u4d70\u4d71\u4d72\u4d73\u4d74\u4d75\u4d76\u4d77\u4d78\u4d79\u4d7a\u4d7b\u4d7c\u4d7d\u4d7e\u4d7f\u4d80\u4d81\u4d82\u4d83\u4d84\u4d85\u4d86\u4d87\u4d88\u4d89\u4d8a\u4d8b\u4d8c\u4d8d\u4d8e\u4d8f\u4d90\u4d91\u4d92\u4d93\u4d94\u4d95\u4d96\u4d97\u4d98\u4d99\u4d9a\u4d9b\u4d9c\u4d9d\u4d9e\u4d9f\u4da0\u4da1\u4da2\u4da3\u4da4\u4da5\u4da6\u4da7\u4da8\u4da9\u4daa\u4dab\u4dac\u4dad\u4dae\u4daf\u4db0\u4db1\u4db2\u4db3\u4db4\u4db5\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d\u4e0e\u4e0f\u4e10\u4e11\u4e12\u4e13\u4e14\u4e15\u4e16\u4e17\u4e18\u4e19\u4e1a\u4e1b\u4e1c\u4e1d\u4e1e\u4e1f\u4e20\u4e21\u4e22\u4e23\u4e24\u4e25\u4e26\u4e27\u4e28\u4e29\u4e2a\u4e2b\u4e2c\u4e2d\u4e2e\u4e2f\u4e30\u4e31\u4e32\u4e33\u4e34\u4e35\u4e36\u4e37\u4e38\u4e39\u4e3a\u4e3b\u4e3c\u4e3d\u4e3e\u4e3f\u4e40\u4e41\u4e42\u4e43\u4e44\u4e45\u4e46\u4e47\u4e48\u4e49\u4e4a\u4e4b\u4e4c\u4e4d\u4e4e\u4e4f\u4e50\u4e51\u4e52\u4e53\u4e54\u4e55\u4e56\u4e57\u4e58\u4e59\u4e5a\u4e5b\u4e5c\u4e5d\u4e5e\u4e5f\u4e60\u4e61\u4e62\u4e63\u4e64\u4e65\u4e66\u4e67\u4e68\u4e69\u4e6a\u4e6b\u4e6c\u4e6d\u4e6e\u4e6f\u4e70\u4e71\u4e72\u4e73\u4e74\u4e75\u4e76\u4e77\u4e78\u4e79\u4e7a\u4e7b\u4e7c\u4e7d\u4e7e\u4e7f\u4e80\u4e81\u4e82\u4e83\u4e84\u4e85\u4e86\u4e87\u4e88\u4e89\u4e8a\u4e8b\u4e8c\u4e8d\u4e8e\u4e8f\u4e90\u4e91\u4e92\u4e93\u4e94\u4e95\u4e96\u4e97\u4e98\u4e99\u4e9a\u4e9b\u4e9c\u4e9d\u4e9e\u4e9f\u4ea0\u4ea1\u4ea2\u4ea3\u4ea4\u4ea5\u4ea6\u4ea7\u4ea8\u4ea9\u4eaa\u4eab\u4eac\u4ead\u4eae\u4eaf\u4eb0\u4eb1\u4eb2\u4eb3\u4eb4\u4eb5\u4eb6\u4eb7\u4eb8\u4eb9\u4eba\u4ebb\u4ebc\u4ebd\u4ebe\u4ebf\u4ec0\u4ec1\u4ec2\u4ec3\u4ec4\u4ec5\u4ec6\u4ec7\u4ec8\u4ec9\u4eca\u4ecb\u4ecc\u4ecd\u4ece\u4ecf\u4ed0\u4ed1\u4ed2\u4ed3\u4ed4\u4ed5\u4ed6\u4ed7\u4ed8\u4ed9\u4eda\u4edb\u4edc\u4edd\u4ede\u4edf\u4ee0\u4ee1\u4ee2\u4ee3\u4ee4\u4ee5\u4ee6\u4ee7\u4ee8\u4ee9\u4eea\u4eeb\u4eec\u4eed\u4eee\u4eef\u4ef0\u4ef1\u4ef2\u4ef3\u4ef4\u4ef5\u4ef6\u4ef7\u4ef8\u4ef9\u4efa\u4efb\u4efc\u4efd\u4efe\u4eff\u4f00\u4f01\u4f02\u4f03\u4f04\u4f05\u4f06\u4f07\u4f08\u4f09\u4f0a\u4f0b\u4f0c\u4f0d\u4f0e\u4f0f\u4f10\u4f11\u4f12\u4f13\u4f14\u4f15\u4f16\u4f17\u4f18\u4f19\u4f1a\u4f1b\u4f1c\u4f1d\u4f1e\u4f1f\u4f20\u4f21\u4f22\u4f23\u4f24\u4f25\u4f26\u4f27\u4f28\u4f29\u4f2a\u4f2b\u4f2c\u4f2d\u4f2e\u4f2f\u4f30\u4f31\u4f32\u4f33\u4f34\u4f35\u4f36\u4f37\u4f38\u4f39\u4f3a\u4f3b\u4f3c\u4f3d\u4f3e\u4f3f\u4f40\u4f41\u4f42\u4f43\u4f44\u4f45\u4f46\u4f47\u4f48\u4f49\u4f4a\u4f4b\u4f4c\u4f4d\u4f4e\u4f4f\u4f50\u4f51\u4f52\u4f53\u4f54\u4f55\u4f56\u4f57\u4f58\u4f59\u4f5a\u4f5b\u4f5c\u4f5d\u4f5e\u4f5f\u4f60\u4f61\u4f62\u4f63\u4f64\u4f65\u4f66\u4f67\u4f68\u4f69\u4f6a\u4f6b\u4f6c\u4f6d\u4f6e\u4f6f\u4f70\u4f71\u4f72\u4f73\u4f74\u4f75\u4f76\u4f77\u4f78\u4f79\u4f7a\u4f7b\u4f7c\u4f7d\u4f7e\u4f7f\u4f80\u4f81\u4f82\u4f83\u4f84\u4f85\u4f86\u4f87\u4f88\u4f89\u4f8a\u4f8b\u4f8c\u4f8d\u4f8e\u4f8f\u4f90\u4f91\u4f92\u4f93\u4f94\u4f95\u4f96\u4f97\u4f98\u4f99\u4f9a\u4f9b\u4f9c\u4f9d\u4f9e\u4f9f\u4fa0\u4fa1\u4fa2\u4fa3\u4fa4\u4fa5\u4fa6\u4fa7\u4fa8\u4fa9\u4faa\u4fab\u4fac\u4fad\u4fae\u4faf\u4fb0\u4fb1\u4fb2\u4fb3\u4fb4\u4fb5\u4fb6\u4fb7\u4fb8\u4fb9\u4fba\u4fbb\u4fbc\u4fbd\u4fbe\u4fbf\u4fc0\u4fc1\u4fc2\u4fc3\u4fc4\u4fc5\u4fc6\u4fc7\u4fc8\u4fc9\u4fca\u4fcb\u4fcc\u4fcd\u4fce\u4fcf\u4fd0\u4fd1\u4fd2\u4fd3\u4fd4\u4fd5\u4fd6\u4fd7\u4fd8\u4fd9\u4fda\u4fdb\u4fdc\u4fdd\u4fde\u4fdf\u4fe0\u4fe1\u4fe2\u4fe3\u4fe4\u4fe5\u4fe6\u4fe7\u4fe8\u4fe9\u4fea\u4feb\u4fec\u4fed\u4fee\u4fef\u4ff0\u4ff1\u4ff2\u4ff3\u4ff4\u4ff5\u4ff6\u4ff7\u4ff8\u4ff9\u4ffa\u4ffb\u4ffc\u4ffd\u4ffe\u4fff\u5000\u5001\u5002\u5003\u5004\u5005\u5006\u5007\u5008\u5009\u500a\u500b\u500c\u500d\u500e\u500f\u5010\u5011\u5012\u5013\u5014\u5015\u5016\u5017\u5018\u5019\u501a\u501b\u501c\u501d\u501e\u501f\u5020\u5021\u5022\u5023\u5024\u5025\u5026\u5027\u5028\u5029\u502a\u502b\u502c\u502d\u502e\u502f\u5030\u5031\u5032\u5033\u5034\u5035\u5036\u5037\u5038\u5039\u503a\u503b\u503c\u503d\u503e\u503f\u5040\u5041\u5042\u5043\u5044\u5045\u5046\u5047\u5048\u5049\u504a\u504b\u504c\u504d\u504e\u504f\u5050\u5051\u5052\u5053\u5054\u5055\u5056\u5057\u5058\u5059\u505a\u505b\u505c\u505d\u505e\u505f\u5060\u5061\u5062\u5063\u5064\u5065\u5066\u5067\u5068\u5069\u506a\u506b\u506c\u506d\u506e\u506f\u5070\u5071\u5072\u5073\u5074\u5075\u5076\u5077\u5078\u5079\u507a\u507b\u507c\u507d\u507e\u507f\u5080\u5081\u5082\u5083\u5084\u5085\u5086\u5087\u5088\u5089\u508a\u508b\u508c\u508d\u508e\u508f\u5090\u5091\u5092\u5093\u5094\u5095\u5096\u5097\u5098\u5099\u509a\u509b\u509c\u509d\u509e\u509f\u50a0\u50a1\u50a2\u50a3\u50a4\u50a5\u50a6\u50a7\u50a8\u50a9\u50aa\u50ab\u50ac\u50ad\u50ae\u50af\u50b0\u50b1\u50b2\u50b3\u50b4\u50b5\u50b6\u50b7\u50b8\u50b9\u50ba\u50bb\u50bc\u50bd\u50be\u50bf\u50c0\u50c1\u50c2\u50c3\u50c4\u50c5\u50c6\u50c7\u50c8\u50c9\u50ca\u50cb\u50cc\u50cd\u50ce\u50cf\u50d0\u50d1\u50d2\u50d3\u50d4\u50d5\u50d6\u50d7\u50d8\u50d9\u50da\u50db\u50dc\u50dd\u50de\u50df\u50e0\u50e1\u50e2\u50e3\u50e4\u50e5\u50e6\u50e7\u50e8\u50e9\u50ea\u50eb\u50ec\u50ed\u50ee\u50ef\u50f0\u50f1\u50f2\u50f3\u50f4\u50f5\u50f6\u50f7\u50f8\u50f9\u50fa\u50fb\u50fc\u50fd\u50fe\u50ff\u5100\u5101\u5102\u5103\u5104\u5105\u5106\u5107\u5108\u5109\u510a\u510b\u510c\u510d\u510e\u510f\u5110\u5111\u5112\u5113\u5114\u5115\u5116\u5117\u5118\u5119\u511a\u511b\u511c\u511d\u511e\u511f\u5120\u5121\u5122\u5123\u5124\u5125\u5126\u5127\u5128\u5129\u512a\u512b\u512c\u512d\u512e\u512f\u5130\u5131\u5132\u5133\u5134\u5135\u5136\u5137\u5138\u5139\u513a\u513b\u513c\u513d\u513e\u513f\u5140\u5141\u5142\u5143\u5144\u5145\u5146\u5147\u5148\u5149\u514a\u514b\u514c\u514d\u514e\u514f\u5150\u5151\u5152\u5153\u5154\u5155\u5156\u5157\u5158\u5159\u515a\u515b\u515c\u515d\u515e\u515f\u5160\u5161\u5162\u5163\u5164\u5165\u5166\u5167\u5168\u5169\u516a\u516b\u516c\u516d\u516e\u516f\u5170\u5171\u5172\u5173\u5174\u5175\u5176\u5177\u5178\u5179\u517a\u517b\u517c\u517d\u517e\u517f\u5180\u5181\u5182\u5183\u5184\u5185\u5186\u5187\u5188\u5189\u518a\u518b\u518c\u518d\u518e\u518f\u5190\u5191\u5192\u5193\u5194\u5195\u5196\u5197\u5198\u5199\u519a\u519b\u519c\u519d\u519e\u519f\u51a0\u51a1\u51a2\u51a3\u51a4\u51a5\u51a6\u51a7\u51a8\u51a9\u51aa\u51ab\u51ac\u51ad\u51ae\u51af\u51b0\u51b1\u51b2\u51b3\u51b4\u51b5\u51b6\u51b7\u51b8\u51b9\u51ba\u51bb\u51bc\u51bd\u51be\u51bf\u51c0\u51c1\u51c2\u51c3\u51c4\u51c5\u51c6\u51c7\u51c8\u51c9\u51ca\u51cb\u51cc\u51cd\u51ce\u51cf\u51d0\u51d1\u51d2\u51d3\u51d4\u51d5\u51d6\u51d7\u51d8\u51d9\u51da\u51db\u51dc\u51dd\u51de\u51df\u51e0\u51e1\u51e2\u51e3\u51e4\u51e5\u51e6\u51e7\u51e8\u51e9\u51ea\u51eb\u51ec\u51ed\u51ee\u51ef\u51f0\u51f1\u51f2\u51f3\u51f4\u51f5\u51f6\u51f7\u51f8\u51f9\u51fa\u51fb\u51fc\u51fd\u51fe\u51ff\u5200\u5201\u5202\u5203\u5204\u5205\u5206\u5207\u5208\u5209\u520a\u520b\u520c\u520d\u520e\u520f\u5210\u5211\u5212\u5213\u5214\u5215\u5216\u5217\u5218\u5219\u521a\u521b\u521c\u521d\u521e\u521f\u5220\u5221\u5222\u5223\u5224\u5225\u5226\u5227\u5228\u5229\u522a\u522b\u522c\u522d\u522e\u522f\u5230\u5231\u5232\u5233\u5234\u5235\u5236\u5237\u5238\u5239\u523a\u523b\u523c\u523d\u523e\u523f\u5240\u5241\u5242\u5243\u5244\u5245\u5246\u5247\u5248\u5249\u524a\u524b\u524c\u524d\u524e\u524f\u5250\u5251\u5252\u5253\u5254\u5255\u5256\u5257\u5258\u5259\u525a\u525b\u525c\u525d\u525e\u525f\u5260\u5261\u5262\u5263\u5264\u5265\u5266\u5267\u5268\u5269\u526a\u526b\u526c\u526d\u526e\u526f\u5270\u5271\u5272\u5273\u5274\u5275\u5276\u5277\u5278\u5279\u527a\u527b\u527c\u527d\u527e\u527f\u5280\u5281\u5282\u5283\u5284\u5285\u5286\u5287\u5288\u5289\u528a\u528b\u528c\u528d\u528e\u528f\u5290\u5291\u5292\u5293\u5294\u5295\u5296\u5297\u5298\u5299\u529a\u529b\u529c\u529d\u529e\u529f\u52a0\u52a1\u52a2\u52a3\u52a4\u52a5\u52a6\u52a7\u52a8\u52a9\u52aa\u52ab\u52ac\u52ad\u52ae\u52af\u52b0\u52b1\u52b2\u52b3\u52b4\u52b5\u52b6\u52b7\u52b8\u52b9\u52ba\u52bb\u52bc\u52bd\u52be\u52bf\u52c0\u52c1\u52c2\u52c3\u52c4\u52c5\u52c6\u52c7\u52c8\u52c9\u52ca\u52cb\u52cc\u52cd\u52ce\u52cf\u52d0\u52d1\u52d2\u52d3\u52d4\u52d5\u52d6\u52d7\u52d8\u52d9\u52da\u52db\u52dc\u52dd\u52de\u52df\u52e0\u52e1\u52e2\u52e3\u52e4\u52e5\u52e6\u52e7\u52e8\u52e9\u52ea\u52eb\u52ec\u52ed\u52ee\u52ef\u52f0\u52f1\u52f2\u52f3\u52f4\u52f5\u52f6\u52f7\u52f8\u52f9\u52fa\u52fb\u52fc\u52fd\u52fe\u52ff\u5300\u5301\u5302\u5303\u5304\u5305\u5306\u5307\u5308\u5309\u530a\u530b\u530c\u530d\u530e\u530f\u5310\u5311\u5312\u5313\u5314\u5315\u5316\u5317\u5318\u5319\u531a\u531b\u531c\u531d\u531e\u531f\u5320\u5321\u5322\u5323\u5324\u5325\u5326\u5327\u5328\u5329\u532a\u532b\u532c\u532d\u532e\u532f\u5330\u5331\u5332\u5333\u5334\u5335\u5336\u5337\u5338\u5339\u533a\u533b\u533c\u533d\u533e\u533f\u5340\u5341\u5342\u5343\u5344\u5345\u5346\u5347\u5348\u5349\u534a\u534b\u534c\u534d\u534e\u534f\u5350\u5351\u5352\u5353\u5354\u5355\u5356\u5357\u5358\u5359\u535a\u535b\u535c\u535d\u535e\u535f\u5360\u5361\u5362\u5363\u5364\u5365\u5366\u5367\u5368\u5369\u536a\u536b\u536c\u536d\u536e\u536f\u5370\u5371\u5372\u5373\u5374\u5375\u5376\u5377\u5378\u5379\u537a\u537b\u537c\u537d\u537e\u537f\u5380\u5381\u5382\u5383\u5384\u5385\u5386\u5387\u5388\u5389\u538a\u538b\u538c\u538d\u538e\u538f\u5390\u5391\u5392\u5393\u5394\u5395\u5396\u5397\u5398\u5399\u539a\u539b\u539c\u539d\u539e\u539f\u53a0\u53a1\u53a2\u53a3\u53a4\u53a5\u53a6\u53a7\u53a8\u53a9\u53aa\u53ab\u53ac\u53ad\u53ae\u53af\u53b0\u53b1\u53b2\u53b3\u53b4\u53b5\u53b6\u53b7\u53b8\u53b9\u53ba\u53bb\u53bc\u53bd\u53be\u53bf\u53c0\u53c1\u53c2\u53c3\u53c4\u53c5\u53c6\u53c7\u53c8\u53c9\u53ca\u53cb\u53cc\u53cd\u53ce\u53cf\u53d0\u53d1\u53d2\u53d3\u53d4\u53d5\u53d6\u53d7\u53d8\u53d9\u53da\u53db\u53dc\u53dd\u53de\u53df\u53e0\u53e1\u53e2\u53e3\u53e4\u53e5\u53e6\u53e7\u53e8\u53e9\u53ea\u53eb\u53ec\u53ed\u53ee\u53ef\u53f0\u53f1\u53f2\u53f3\u53f4\u53f5\u53f6\u53f7\u53f8\u53f9\u53fa\u53fb\u53fc\u53fd\u53fe\u53ff\u5400\u5401\u5402\u5403\u5404\u5405\u5406\u5407\u5408\u5409\u540a\u540b\u540c\u540d\u540e\u540f\u5410\u5411\u5412\u5413\u5414\u5415\u5416\u5417\u5418\u5419\u541a\u541b\u541c\u541d\u541e\u541f\u5420\u5421\u5422\u5423\u5424\u5425\u5426\u5427\u5428\u5429\u542a\u542b\u542c\u542d\u542e\u542f\u5430\u5431\u5432\u5433\u5434\u5435\u5436\u5437\u5438\u5439\u543a\u543b\u543c\u543d\u543e\u543f\u5440\u5441\u5442\u5443\u5444\u5445\u5446\u5447\u5448\u5449\u544a\u544b\u544c\u544d\u544e\u544f\u5450\u5451\u5452\u5453\u5454\u5455\u5456\u5457\u5458\u5459\u545a\u545b\u545c\u545d\u545e\u545f\u5460\u5461\u5462\u5463\u5464\u5465\u5466\u5467\u5468\u5469\u546a\u546b\u546c\u546d\u546e\u546f\u5470\u5471\u5472\u5473\u5474\u5475\u5476\u5477\u5478\u5479\u547a\u547b\u547c\u547d\u547e\u547f\u5480\u5481\u5482\u5483\u5484\u5485\u5486\u5487\u5488\u5489\u548a\u548b\u548c\u548d\u548e\u548f\u5490\u5491\u5492\u5493\u5494\u5495\u5496\u5497\u5498\u5499\u549a\u549b\u549c\u549d\u549e\u549f\u54a0\u54a1\u54a2\u54a3\u54a4\u54a5\u54a6\u54a7\u54a8\u54a9\u54aa\u54ab\u54ac\u54ad\u54ae\u54af\u54b0\u54b1\u54b2\u54b3\u54b4\u54b5\u54b6\u54b7\u54b8\u54b9\u54ba\u54bb\u54bc\u54bd\u54be\u54bf\u54c0\u54c1\u54c2\u54c3\u54c4\u54c5\u54c6\u54c7\u54c8\u54c9\u54ca\u54cb\u54cc\u54cd\u54ce\u54cf\u54d0\u54d1\u54d2\u54d3\u54d4\u54d5\u54d6\u54d7\u54d8\u54d9\u54da\u54db\u54dc\u54dd\u54de\u54df\u54e0\u54e1\u54e2\u54e3\u54e4\u54e5\u54e6\u54e7\u54e8\u54e9\u54ea\u54eb\u54ec\u54ed\u54ee\u54ef\u54f0\u54f1\u54f2\u54f3\u54f4\u54f5\u54f6\u54f7\u54f8\u54f9\u54fa\u54fb\u54fc\u54fd\u54fe\u54ff\u5500\u5501\u5502\u5503\u5504\u5505\u5506\u5507\u5508\u5509\u550a\u550b\u550c\u550d\u550e\u550f\u5510\u5511\u5512\u5513\u5514\u5515\u5516\u5517\u5518\u5519\u551a\u551b\u551c\u551d\u551e\u551f\u5520\u5521\u5522\u5523\u5524\u5525\u5526\u5527\u5528\u5529\u552a\u552b\u552c\u552d\u552e\u552f\u5530\u5531\u5532\u5533\u5534\u5535\u5536\u5537\u5538\u5539\u553a\u553b\u553c\u553d\u553e\u553f\u5540\u5541\u5542\u5543\u5544\u5545\u5546\u5547\u5548\u5549\u554a\u554b\u554c\u554d\u554e\u554f\u5550\u5551\u5552\u5553\u5554\u5555\u5556\u5557\u5558\u5559\u555a\u555b\u555c\u555d\u555e\u555f\u5560\u5561\u5562\u5563\u5564\u5565\u5566\u5567\u5568\u5569\u556a\u556b\u556c\u556d\u556e\u556f\u5570\u5571\u5572\u5573\u5574\u5575\u5576\u5577\u5578\u5579\u557a\u557b\u557c\u557d\u557e\u557f\u5580\u5581\u5582\u5583\u5584\u5585\u5586\u5587\u5588\u5589\u558a\u558b\u558c\u558d\u558e\u558f\u5590\u5591\u5592\u5593\u5594\u5595\u5596\u5597\u5598\u5599\u559a\u559b\u559c\u559d\u559e\u559f\u55a0\u55a1\u55a2\u55a3\u55a4\u55a5\u55a6\u55a7\u55a8\u55a9\u55aa\u55ab\u55ac\u55ad\u55ae\u55af\u55b0\u55b1\u55b2\u55b3\u55b4\u55b5\u55b6\u55b7\u55b8\u55b9\u55ba\u55bb\u55bc\u55bd\u55be\u55bf\u55c0\u55c1\u55c2\u55c3\u55c4\u55c5\u55c6\u55c7\u55c8\u55c9\u55ca\u55cb\u55cc\u55cd\u55ce\u55cf\u55d0\u55d1\u55d2\u55d3\u55d4\u55d5\u55d6\u55d7\u55d8\u55d9\u55da\u55db\u55dc\u55dd\u55de\u55df\u55e0\u55e1\u55e2\u55e3\u55e4\u55e5\u55e6\u55e7\u55e8\u55e9\u55ea\u55eb\u55ec\u55ed\u55ee\u55ef\u55f0\u55f1\u55f2\u55f3\u55f4\u55f5\u55f6\u55f7\u55f8\u55f9\u55fa\u55fb\u55fc\u55fd\u55fe\u55ff\u5600\u5601\u5602\u5603\u5604\u5605\u5606\u5607\u5608\u5609\u560a\u560b\u560c\u560d\u560e\u560f\u5610\u5611\u5612\u5613\u5614\u5615\u5616\u5617\u5618\u5619\u561a\u561b\u561c\u561d\u561e\u561f\u5620\u5621\u5622\u5623\u5624\u5625\u5626\u5627\u5628\u5629\u562a\u562b\u562c\u562d\u562e\u562f\u5630\u5631\u5632\u5633\u5634\u5635\u5636\u5637\u5638\u5639\u563a\u563b\u563c\u563d\u563e\u563f\u5640\u5641\u5642\u5643\u5644\u5645\u5646\u5647\u5648\u5649\u564a\u564b\u564c\u564d\u564e\u564f\u5650\u5651\u5652\u5653\u5654\u5655\u5656\u5657\u5658\u5659\u565a\u565b\u565c\u565d\u565e\u565f\u5660\u5661\u5662\u5663\u5664\u5665\u5666\u5667\u5668\u5669\u566a\u566b\u566c\u566d\u566e\u566f\u5670\u5671\u5672\u5673\u5674\u5675\u5676\u5677\u5678\u5679\u567a\u567b\u567c\u567d\u567e\u567f\u5680\u5681\u5682\u5683\u5684\u5685\u5686\u5687\u5688\u5689\u568a\u568b\u568c\u568d\u568e\u568f\u5690\u5691\u5692\u5693\u5694\u5695\u5696\u5697\u5698\u5699\u569a\u569b\u569c\u569d\u569e\u569f\u56a0\u56a1\u56a2\u56a3\u56a4\u56a5\u56a6\u56a7\u56a8\u56a9\u56aa\u56ab\u56ac\u56ad\u56ae\u56af\u56b0\u56b1\u56b2\u56b3\u56b4\u56b5\u56b6\u56b7\u56b8\u56b9\u56ba\u56bb\u56bc\u56bd\u56be\u56bf\u56c0\u56c1\u56c2\u56c3\u56c4\u56c5\u56c6\u56c7\u56c8\u56c9\u56ca\u56cb\u56cc\u56cd\u56ce\u56cf\u56d0\u56d1\u56d2\u56d3\u56d4\u56d5\u56d6\u56d7\u56d8\u56d9\u56da\u56db\u56dc\u56dd\u56de\u56df\u56e0\u56e1\u56e2\u56e3\u56e4\u56e5\u56e6\u56e7\u56e8\u56e9\u56ea\u56eb\u56ec\u56ed\u56ee\u56ef\u56f0\u56f1\u56f2\u56f3\u56f4\u56f5\u56f6\u56f7\u56f8\u56f9\u56fa\u56fb\u56fc\u56fd\u56fe\u56ff\u5700\u5701\u5702\u5703\u5704\u5705\u5706\u5707\u5708\u5709\u570a\u570b\u570c\u570d\u570e\u570f\u5710\u5711\u5712\u5713\u5714\u5715\u5716\u5717\u5718\u5719\u571a\u571b\u571c\u571d\u571e\u571f\u5720\u5721\u5722\u5723\u5724\u5725\u5726\u5727\u5728\u5729\u572a\u572b\u572c\u572d\u572e\u572f\u5730\u5731\u5732\u5733\u5734\u5735\u5736\u5737\u5738\u5739\u573a\u573b\u573c\u573d\u573e\u573f\u5740\u5741\u5742\u5743\u5744\u5745\u5746\u5747\u5748\u5749\u574a\u574b\u574c\u574d\u574e\u574f\u5750\u5751\u5752\u5753\u5754\u5755\u5756\u5757\u5758\u5759\u575a\u575b\u575c\u575d\u575e\u575f\u5760\u5761\u5762\u5763\u5764\u5765\u5766\u5767\u5768\u5769\u576a\u576b\u576c\u576d\u576e\u576f\u5770\u5771\u5772\u5773\u5774\u5775\u5776\u5777\u5778\u5779\u577a\u577b\u577c\u577d\u577e\u577f\u5780\u5781\u5782\u5783\u5784\u5785\u5786\u5787\u5788\u5789\u578a\u578b\u578c\u578d\u578e\u578f\u5790\u5791\u5792\u5793\u5794\u5795\u5796\u5797\u5798\u5799\u579a\u579b\u579c\u579d\u579e\u579f\u57a0\u57a1\u57a2\u57a3\u57a4\u57a5\u57a6\u57a7\u57a8\u57a9\u57aa\u57ab\u57ac\u57ad\u57ae\u57af\u57b0\u57b1\u57b2\u57b3\u57b4\u57b5\u57b6\u57b7\u57b8\u57b9\u57ba\u57bb\u57bc\u57bd\u57be\u57bf\u57c0\u57c1\u57c2\u57c3\u57c4\u57c5\u57c6\u57c7\u57c8\u57c9\u57ca\u57cb\u57cc\u57cd\u57ce\u57cf\u57d0\u57d1\u57d2\u57d3\u57d4\u57d5\u57d6\u57d7\u57d8\u57d9\u57da\u57db\u57dc\u57dd\u57de\u57df\u57e0\u57e1\u57e2\u57e3\u57e4\u57e5\u57e6\u57e7\u57e8\u57e9\u57ea\u57eb\u57ec\u57ed\u57ee\u57ef\u57f0\u57f1\u57f2\u57f3\u57f4\u57f5\u57f6\u57f7\u57f8\u57f9\u57fa\u57fb\u57fc\u57fd\u57fe\u57ff\u5800\u5801\u5802\u5803\u5804\u5805\u5806\u5807\u5808\u5809\u580a\u580b\u580c\u580d\u580e\u580f\u5810\u5811\u5812\u5813\u5814\u5815\u5816\u5817\u5818\u5819\u581a\u581b\u581c\u581d\u581e\u581f\u5820\u5821\u5822\u5823\u5824\u5825\u5826\u5827\u5828\u5829\u582a\u582b\u582c\u582d\u582e\u582f\u5830\u5831\u5832\u5833\u5834\u5835\u5836\u5837\u5838\u5839\u583a\u583b\u583c\u583d\u583e\u583f\u5840\u5841\u5842\u5843\u5844\u5845\u5846\u5847\u5848\u5849\u584a\u584b\u584c\u584d\u584e\u584f\u5850\u5851\u5852\u5853\u5854\u5855\u5856\u5857\u5858\u5859\u585a\u585b\u585c\u585d\u585e\u585f\u5860\u5861\u5862\u5863\u5864\u5865\u5866\u5867\u5868\u5869\u586a\u586b\u586c\u586d\u586e\u586f\u5870\u5871\u5872\u5873\u5874\u5875\u5876\u5877\u5878\u5879\u587a\u587b\u587c\u587d\u587e\u587f\u5880\u5881\u5882\u5883\u5884\u5885\u5886\u5887\u5888\u5889\u588a\u588b\u588c\u588d\u588e\u588f\u5890\u5891\u5892\u5893\u5894\u5895\u5896\u5897\u5898\u5899\u589a\u589b\u589c\u589d\u589e\u589f\u58a0\u58a1\u58a2\u58a3\u58a4\u58a5\u58a6\u58a7\u58a8\u58a9\u58aa\u58ab\u58ac\u58ad\u58ae\u58af\u58b0\u58b1\u58b2\u58b3\u58b4\u58b5\u58b6\u58b7\u58b8\u58b9\u58ba\u58bb\u58bc\u58bd\u58be\u58bf\u58c0\u58c1\u58c2\u58c3\u58c4\u58c5\u58c6\u58c7\u58c8\u58c9\u58ca\u58cb\u58cc\u58cd\u58ce\u58cf\u58d0\u58d1\u58d2\u58d3\u58d4\u58d5\u58d6\u58d7\u58d8\u58d9\u58da\u58db\u58dc\u58dd\u58de\u58df\u58e0\u58e1\u58e2\u58e3\u58e4\u58e5\u58e6\u58e7\u58e8\u58e9\u58ea\u58eb\u58ec\u58ed\u58ee\u58ef\u58f0\u58f1\u58f2\u58f3\u58f4\u58f5\u58f6\u58f7\u58f8\u58f9\u58fa\u58fb\u58fc\u58fd\u58fe\u58ff\u5900\u5901\u5902\u5903\u5904\u5905\u5906\u5907\u5908\u5909\u590a\u590b\u590c\u590d\u590e\u590f\u5910\u5911\u5912\u5913\u5914\u5915\u5916\u5917\u5918\u5919\u591a\u591b\u591c\u591d\u591e\u591f\u5920\u5921\u5922\u5923\u5924\u5925\u5926\u5927\u5928\u5929\u592a\u592b\u592c\u592d\u592e\u592f\u5930\u5931\u5932\u5933\u5934\u5935\u5936\u5937\u5938\u5939\u593a\u593b\u593c\u593d\u593e\u593f\u5940\u5941\u5942\u5943\u5944\u5945\u5946\u5947\u5948\u5949\u594a\u594b\u594c\u594d\u594e\u594f\u5950\u5951\u5952\u5953\u5954\u5955\u5956\u5957\u5958\u5959\u595a\u595b\u595c\u595d\u595e\u595f\u5960\u5961\u5962\u5963\u5964\u5965\u5966\u5967\u5968\u5969\u596a\u596b\u596c\u596d\u596e\u596f\u5970\u5971\u5972\u5973\u5974\u5975\u5976\u5977\u5978\u5979\u597a\u597b\u597c\u597d\u597e\u597f\u5980\u5981\u5982\u5983\u5984\u5985\u5986\u5987\u5988\u5989\u598a\u598b\u598c\u598d\u598e\u598f\u5990\u5991\u5992\u5993\u5994\u5995\u5996\u5997\u5998\u5999\u599a\u599b\u599c\u599d\u599e\u599f\u59a0\u59a1\u59a2\u59a3\u59a4\u59a5\u59a6\u59a7\u59a8\u59a9\u59aa\u59ab\u59ac\u59ad\u59ae\u59af\u59b0\u59b1\u59b2\u59b3\u59b4\u59b5\u59b6\u59b7\u59b8\u59b9\u59ba\u59bb\u59bc\u59bd\u59be\u59bf\u59c0\u59c1\u59c2\u59c3\u59c4\u59c5\u59c6\u59c7\u59c8\u59c9\u59ca\u59cb\u59cc\u59cd\u59ce\u59cf\u59d0\u59d1\u59d2\u59d3\u59d4\u59d5\u59d6\u59d7\u59d8\u59d9\u59da\u59db\u59dc\u59dd\u59de\u59df\u59e0\u59e1\u59e2\u59e3\u59e4\u59e5\u59e6\u59e7\u59e8\u59e9\u59ea\u59eb\u59ec\u59ed\u59ee\u59ef\u59f0\u59f1\u59f2\u59f3\u59f4\u59f5\u59f6\u59f7\u59f8\u59f9\u59fa\u59fb\u59fc\u59fd\u59fe\u59ff\u5a00\u5a01\u5a02\u5a03\u5a04\u5a05\u5a06\u5a07\u5a08\u5a09\u5a0a\u5a0b\u5a0c\u5a0d\u5a0e\u5a0f\u5a10\u5a11\u5a12\u5a13\u5a14\u5a15\u5a16\u5a17\u5a18\u5a19\u5a1a\u5a1b\u5a1c\u5a1d\u5a1e\u5a1f\u5a20\u5a21\u5a22\u5a23\u5a24\u5a25\u5a26\u5a27\u5a28\u5a29\u5a2a\u5a2b\u5a2c\u5a2d\u5a2e\u5a2f\u5a30\u5a31\u5a32\u5a33\u5a34\u5a35\u5a36\u5a37\u5a38\u5a39\u5a3a\u5a3b\u5a3c\u5a3d\u5a3e\u5a3f\u5a40\u5a41\u5a42\u5a43\u5a44\u5a45\u5a46\u5a47\u5a48\u5a49\u5a4a\u5a4b\u5a4c\u5a4d\u5a4e\u5a4f\u5a50\u5a51\u5a52\u5a53\u5a54\u5a55\u5a56\u5a57\u5a58\u5a59\u5a5a\u5a5b\u5a5c\u5a5d\u5a5e\u5a5f\u5a60\u5a61\u5a62\u5a63\u5a64\u5a65\u5a66\u5a67\u5a68\u5a69\u5a6a\u5a6b\u5a6c\u5a6d\u5a6e\u5a6f\u5a70\u5a71\u5a72\u5a73\u5a74\u5a75\u5a76\u5a77\u5a78\u5a79\u5a7a\u5a7b\u5a7c\u5a7d\u5a7e\u5a7f\u5a80\u5a81\u5a82\u5a83\u5a84\u5a85\u5a86\u5a87\u5a88\u5a89\u5a8a\u5a8b\u5a8c\u5a8d\u5a8e\u5a8f\u5a90\u5a91\u5a92\u5a93\u5a94\u5a95\u5a96\u5a97\u5a98\u5a99\u5a9a\u5a9b\u5a9c\u5a9d\u5a9e\u5a9f\u5aa0\u5aa1\u5aa2\u5aa3\u5aa4\u5aa5\u5aa6\u5aa7\u5aa8\u5aa9\u5aaa\u5aab\u5aac\u5aad\u5aae\u5aaf\u5ab0\u5ab1\u5ab2\u5ab3\u5ab4\u5ab5\u5ab6\u5ab7\u5ab8\u5ab9\u5aba\u5abb\u5abc\u5abd\u5abe\u5abf\u5ac0\u5ac1\u5ac2\u5ac3\u5ac4\u5ac5\u5ac6\u5ac7\u5ac8\u5ac9\u5aca\u5acb\u5acc\u5acd\u5ace\u5acf\u5ad0\u5ad1\u5ad2\u5ad3\u5ad4\u5ad5\u5ad6\u5ad7\u5ad8\u5ad9\u5ada\u5adb\u5adc\u5add\u5ade\u5adf\u5ae0\u5ae1\u5ae2\u5ae3\u5ae4\u5ae5\u5ae6\u5ae7\u5ae8\u5ae9\u5aea\u5aeb\u5aec\u5aed\u5aee\u5aef\u5af0\u5af1\u5af2\u5af3\u5af4\u5af5\u5af6\u5af7\u5af8\u5af9\u5afa\u5afb\u5afc\u5afd\u5afe\u5aff\u5b00\u5b01\u5b02\u5b03\u5b04\u5b05\u5b06\u5b07\u5b08\u5b09\u5b0a\u5b0b\u5b0c\u5b0d\u5b0e\u5b0f\u5b10\u5b11\u5b12\u5b13\u5b14\u5b15\u5b16\u5b17\u5b18\u5b19\u5b1a\u5b1b\u5b1c\u5b1d\u5b1e\u5b1f\u5b20\u5b21\u5b22\u5b23\u5b24\u5b25\u5b26\u5b27\u5b28\u5b29\u5b2a\u5b2b\u5b2c\u5b2d\u5b2e\u5b2f\u5b30\u5b31\u5b32\u5b33\u5b34\u5b35\u5b36\u5b37\u5b38\u5b39\u5b3a\u5b3b\u5b3c\u5b3d\u5b3e\u5b3f\u5b40\u5b41\u5b42\u5b43\u5b44\u5b45\u5b46\u5b47\u5b48\u5b49\u5b4a\u5b4b\u5b4c\u5b4d\u5b4e\u5b4f\u5b50\u5b51\u5b52\u5b53\u5b54\u5b55\u5b56\u5b57\u5b58\u5b59\u5b5a\u5b5b\u5b5c\u5b5d\u5b5e\u5b5f\u5b60\u5b61\u5b62\u5b63\u5b64\u5b65\u5b66\u5b67\u5b68\u5b69\u5b6a\u5b6b\u5b6c\u5b6d\u5b6e\u5b6f\u5b70\u5b71\u5b72\u5b73\u5b74\u5b75\u5b76\u5b77\u5b78\u5b79\u5b7a\u5b7b\u5b7c\u5b7d\u5b7e\u5b7f\u5b80\u5b81\u5b82\u5b83\u5b84\u5b85\u5b86\u5b87\u5b88\u5b89\u5b8a\u5b8b\u5b8c\u5b8d\u5b8e\u5b8f\u5b90\u5b91\u5b92\u5b93\u5b94\u5b95\u5b96\u5b97\u5b98\u5b99\u5b9a\u5b9b\u5b9c\u5b9d\u5b9e\u5b9f\u5ba0\u5ba1\u5ba2\u5ba3\u5ba4\u5ba5\u5ba6\u5ba7\u5ba8\u5ba9\u5baa\u5bab\u5bac\u5bad\u5bae\u5baf\u5bb0\u5bb1\u5bb2\u5bb3\u5bb4\u5bb5\u5bb6\u5bb7\u5bb8\u5bb9\u5bba\u5bbb\u5bbc\u5bbd\u5bbe\u5bbf\u5bc0\u5bc1\u5bc2\u5bc3\u5bc4\u5bc5\u5bc6\u5bc7\u5bc8\u5bc9\u5bca\u5bcb\u5bcc\u5bcd\u5bce\u5bcf\u5bd0\u5bd1\u5bd2\u5bd3\u5bd4\u5bd5\u5bd6\u5bd7\u5bd8\u5bd9\u5bda\u5bdb\u5bdc\u5bdd\u5bde\u5bdf\u5be0\u5be1\u5be2\u5be3\u5be4\u5be5\u5be6\u5be7\u5be8\u5be9\u5bea\u5beb\u5bec\u5bed\u5bee\u5bef\u5bf0\u5bf1\u5bf2\u5bf3\u5bf4\u5bf5\u5bf6\u5bf7\u5bf8\u5bf9\u5bfa\u5bfb\u5bfc\u5bfd\u5bfe\u5bff\u5c00\u5c01\u5c02\u5c03\u5c04\u5c05\u5c06\u5c07\u5c08\u5c09\u5c0a\u5c0b\u5c0c\u5c0d\u5c0e\u5c0f\u5c10\u5c11\u5c12\u5c13\u5c14\u5c15\u5c16\u5c17\u5c18\u5c19\u5c1a\u5c1b\u5c1c\u5c1d\u5c1e\u5c1f\u5c20\u5c21\u5c22\u5c23\u5c24\u5c25\u5c26\u5c27\u5c28\u5c29\u5c2a\u5c2b\u5c2c\u5c2d\u5c2e\u5c2f\u5c30\u5c31\u5c32\u5c33\u5c34\u5c35\u5c36\u5c37\u5c38\u5c39\u5c3a\u5c3b\u5c3c\u5c3d\u5c3e\u5c3f\u5c40\u5c41\u5c42\u5c43\u5c44\u5c45\u5c46\u5c47\u5c48\u5c49\u5c4a\u5c4b\u5c4c\u5c4d\u5c4e\u5c4f\u5c50\u5c51\u5c52\u5c53\u5c54\u5c55\u5c56\u5c57\u5c58\u5c59\u5c5a\u5c5b\u5c5c\u5c5d\u5c5e\u5c5f\u5c60\u5c61\u5c62\u5c63\u5c64\u5c65\u5c66\u5c67\u5c68\u5c69\u5c6a\u5c6b\u5c6c\u5c6d\u5c6e\u5c6f\u5c70\u5c71\u5c72\u5c73\u5c74\u5c75\u5c76\u5c77\u5c78\u5c79\u5c7a\u5c7b\u5c7c\u5c7d\u5c7e\u5c7f\u5c80\u5c81\u5c82\u5c83\u5c84\u5c85\u5c86\u5c87\u5c88\u5c89\u5c8a\u5c8b\u5c8c\u5c8d\u5c8e\u5c8f\u5c90\u5c91\u5c92\u5c93\u5c94\u5c95\u5c96\u5c97\u5c98\u5c99\u5c9a\u5c9b\u5c9c\u5c9d\u5c9e\u5c9f\u5ca0\u5ca1\u5ca2\u5ca3\u5ca4\u5ca5\u5ca6\u5ca7\u5ca8\u5ca9\u5caa\u5cab\u5cac\u5cad\u5cae\u5caf\u5cb0\u5cb1\u5cb2\u5cb3\u5cb4\u5cb5\u5cb6\u5cb7\u5cb8\u5cb9\u5cba\u5cbb\u5cbc\u5cbd\u5cbe\u5cbf\u5cc0\u5cc1\u5cc2\u5cc3\u5cc4\u5cc5\u5cc6\u5cc7\u5cc8\u5cc9\u5cca\u5ccb\u5ccc\u5ccd\u5cce\u5ccf\u5cd0\u5cd1\u5cd2\u5cd3\u5cd4\u5cd5\u5cd6\u5cd7\u5cd8\u5cd9\u5cda\u5cdb\u5cdc\u5cdd\u5cde\u5cdf\u5ce0\u5ce1\u5ce2\u5ce3\u5ce4\u5ce5\u5ce6\u5ce7\u5ce8\u5ce9\u5cea\u5ceb\u5cec\u5ced\u5cee\u5cef\u5cf0\u5cf1\u5cf2\u5cf3\u5cf4\u5cf5\u5cf6\u5cf7\u5cf8\u5cf9\u5cfa\u5cfb\u5cfc\u5cfd\u5cfe\u5cff\u5d00\u5d01\u5d02\u5d03\u5d04\u5d05\u5d06\u5d07\u5d08\u5d09\u5d0a\u5d0b\u5d0c\u5d0d\u5d0e\u5d0f\u5d10\u5d11\u5d12\u5d13\u5d14\u5d15\u5d16\u5d17\u5d18\u5d19\u5d1a\u5d1b\u5d1c\u5d1d\u5d1e\u5d1f\u5d20\u5d21\u5d22\u5d23\u5d24\u5d25\u5d26\u5d27\u5d28\u5d29\u5d2a\u5d2b\u5d2c\u5d2d\u5d2e\u5d2f\u5d30\u5d31\u5d32\u5d33\u5d34\u5d35\u5d36\u5d37\u5d38\u5d39\u5d3a\u5d3b\u5d3c\u5d3d\u5d3e\u5d3f\u5d40\u5d41\u5d42\u5d43\u5d44\u5d45\u5d46\u5d47\u5d48\u5d49\u5d4a\u5d4b\u5d4c\u5d4d\u5d4e\u5d4f\u5d50\u5d51\u5d52\u5d53\u5d54\u5d55\u5d56\u5d57\u5d58\u5d59\u5d5a\u5d5b\u5d5c\u5d5d\u5d5e\u5d5f\u5d60\u5d61\u5d62\u5d63\u5d64\u5d65\u5d66\u5d67\u5d68\u5d69\u5d6a\u5d6b\u5d6c\u5d6d\u5d6e\u5d6f\u5d70\u5d71\u5d72\u5d73\u5d74\u5d75\u5d76\u5d77\u5d78\u5d79\u5d7a\u5d7b\u5d7c\u5d7d\u5d7e\u5d7f\u5d80\u5d81\u5d82\u5d83\u5d84\u5d85\u5d86\u5d87\u5d88\u5d89\u5d8a\u5d8b\u5d8c\u5d8d\u5d8e\u5d8f\u5d90\u5d91\u5d92\u5d93\u5d94\u5d95\u5d96\u5d97\u5d98\u5d99\u5d9a\u5d9b\u5d9c\u5d9d\u5d9e\u5d9f\u5da0\u5da1\u5da2\u5da3\u5da4\u5da5\u5da6\u5da7\u5da8\u5da9\u5daa\u5dab\u5dac\u5dad\u5dae\u5daf\u5db0\u5db1\u5db2\u5db3\u5db4\u5db5\u5db6\u5db7\u5db8\u5db9\u5dba\u5dbb\u5dbc\u5dbd\u5dbe\u5dbf\u5dc0\u5dc1\u5dc2\u5dc3\u5dc4\u5dc5\u5dc6\u5dc7\u5dc8\u5dc9\u5dca\u5dcb\u5dcc\u5dcd\u5dce\u5dcf\u5dd0\u5dd1\u5dd2\u5dd3\u5dd4\u5dd5\u5dd6\u5dd7\u5dd8\u5dd9\u5dda\u5ddb\u5ddc\u5ddd\u5dde\u5ddf\u5de0\u5de1\u5de2\u5de3\u5de4\u5de5\u5de6\u5de7\u5de8\u5de9\u5dea\u5deb\u5dec\u5ded\u5dee\u5def\u5df0\u5df1\u5df2\u5df3\u5df4\u5df5\u5df6\u5df7\u5df8\u5df9\u5dfa\u5dfb\u5dfc\u5dfd\u5dfe\u5dff\u5e00\u5e01\u5e02\u5e03\u5e04\u5e05\u5e06\u5e07\u5e08\u5e09\u5e0a\u5e0b\u5e0c\u5e0d\u5e0e\u5e0f\u5e10\u5e11\u5e12\u5e13\u5e14\u5e15\u5e16\u5e17\u5e18\u5e19\u5e1a\u5e1b\u5e1c\u5e1d\u5e1e\u5e1f\u5e20\u5e21\u5e22\u5e23\u5e24\u5e25\u5e26\u5e27\u5e28\u5e29\u5e2a\u5e2b\u5e2c\u5e2d\u5e2e\u5e2f\u5e30\u5e31\u5e32\u5e33\u5e34\u5e35\u5e36\u5e37\u5e38\u5e39\u5e3a\u5e3b\u5e3c\u5e3d\u5e3e\u5e3f\u5e40\u5e41\u5e42\u5e43\u5e44\u5e45\u5e46\u5e47\u5e48\u5e49\u5e4a\u5e4b\u5e4c\u5e4d\u5e4e\u5e4f\u5e50\u5e51\u5e52\u5e53\u5e54\u5e55\u5e56\u5e57\u5e58\u5e59\u5e5a\u5e5b\u5e5c\u5e5d\u5e5e\u5e5f\u5e60\u5e61\u5e62\u5e63\u5e64\u5e65\u5e66\u5e67\u5e68\u5e69\u5e6a\u5e6b\u5e6c\u5e6d\u5e6e\u5e6f\u5e70\u5e71\u5e72\u5e73\u5e74\u5e75\u5e76\u5e77\u5e78\u5e79\u5e7a\u5e7b\u5e7c\u5e7d\u5e7e\u5e7f\u5e80\u5e81\u5e82\u5e83\u5e84\u5e85\u5e86\u5e87\u5e88\u5e89\u5e8a\u5e8b\u5e8c\u5e8d\u5e8e\u5e8f\u5e90\u5e91\u5e92\u5e93\u5e94\u5e95\u5e96\u5e97\u5e98\u5e99\u5e9a\u5e9b\u5e9c\u5e9d\u5e9e\u5e9f\u5ea0\u5ea1\u5ea2\u5ea3\u5ea4\u5ea5\u5ea6\u5ea7\u5ea8\u5ea9\u5eaa\u5eab\u5eac\u5ead\u5eae\u5eaf\u5eb0\u5eb1\u5eb2\u5eb3\u5eb4\u5eb5\u5eb6\u5eb7\u5eb8\u5eb9\u5eba\u5ebb\u5ebc\u5ebd\u5ebe\u5ebf\u5ec0\u5ec1\u5ec2\u5ec3\u5ec4\u5ec5\u5ec6\u5ec7\u5ec8\u5ec9\u5eca\u5ecb\u5ecc\u5ecd\u5ece\u5ecf\u5ed0\u5ed1\u5ed2\u5ed3\u5ed4\u5ed5\u5ed6\u5ed7\u5ed8\u5ed9\u5eda\u5edb\u5edc\u5edd\u5ede\u5edf\u5ee0\u5ee1\u5ee2\u5ee3\u5ee4\u5ee5\u5ee6\u5ee7\u5ee8\u5ee9\u5eea\u5eeb\u5eec\u5eed\u5eee\u5eef\u5ef0\u5ef1\u5ef2\u5ef3\u5ef4\u5ef5\u5ef6\u5ef7\u5ef8\u5ef9\u5efa\u5efb\u5efc\u5efd\u5efe\u5eff\u5f00\u5f01\u5f02\u5f03\u5f04\u5f05\u5f06\u5f07\u5f08\u5f09\u5f0a\u5f0b\u5f0c\u5f0d\u5f0e\u5f0f\u5f10\u5f11\u5f12\u5f13\u5f14\u5f15\u5f16\u5f17\u5f18\u5f19\u5f1a\u5f1b\u5f1c\u5f1d\u5f1e\u5f1f\u5f20\u5f21\u5f22\u5f23\u5f24\u5f25\u5f26\u5f27\u5f28\u5f29\u5f2a\u5f2b\u5f2c\u5f2d\u5f2e\u5f2f\u5f30\u5f31\u5f32\u5f33\u5f34\u5f35\u5f36\u5f37\u5f38\u5f39\u5f3a\u5f3b\u5f3c\u5f3d\u5f3e\u5f3f\u5f40\u5f41\u5f42\u5f43\u5f44\u5f45\u5f46\u5f47\u5f48\u5f49\u5f4a\u5f4b\u5f4c\u5f4d\u5f4e\u5f4f\u5f50\u5f51\u5f52\u5f53\u5f54\u5f55\u5f56\u5f57\u5f58\u5f59\u5f5a\u5f5b\u5f5c\u5f5d\u5f5e\u5f5f\u5f60\u5f61\u5f62\u5f63\u5f64\u5f65\u5f66\u5f67\u5f68\u5f69\u5f6a\u5f6b\u5f6c\u5f6d\u5f6e\u5f6f\u5f70\u5f71\u5f72\u5f73\u5f74\u5f75\u5f76\u5f77\u5f78\u5f79\u5f7a\u5f7b\u5f7c\u5f7d\u5f7e\u5f7f\u5f80\u5f81\u5f82\u5f83\u5f84\u5f85\u5f86\u5f87\u5f88\u5f89\u5f8a\u5f8b\u5f8c\u5f8d\u5f8e\u5f8f\u5f90\u5f91\u5f92\u5f93\u5f94\u5f95\u5f96\u5f97\u5f98\u5f99\u5f9a\u5f9b\u5f9c\u5f9d\u5f9e\u5f9f\u5fa0\u5fa1\u5fa2\u5fa3\u5fa4\u5fa5\u5fa6\u5fa7\u5fa8\u5fa9\u5faa\u5fab\u5fac\u5fad\u5fae\u5faf\u5fb0\u5fb1\u5fb2\u5fb3\u5fb4\u5fb5\u5fb6\u5fb7\u5fb8\u5fb9\u5fba\u5fbb\u5fbc\u5fbd\u5fbe\u5fbf\u5fc0\u5fc1\u5fc2\u5fc3\u5fc4\u5fc5\u5fc6\u5fc7\u5fc8\u5fc9\u5fca\u5fcb\u5fcc\u5fcd\u5fce\u5fcf\u5fd0\u5fd1\u5fd2\u5fd3\u5fd4\u5fd5\u5fd6\u5fd7\u5fd8\u5fd9\u5fda\u5fdb\u5fdc\u5fdd\u5fde\u5fdf\u5fe0\u5fe1\u5fe2\u5fe3\u5fe4\u5fe5\u5fe6\u5fe7\u5fe8\u5fe9\u5fea\u5feb\u5fec\u5fed\u5fee\u5fef\u5ff0\u5ff1\u5ff2\u5ff3\u5ff4\u5ff5\u5ff6\u5ff7\u5ff8\u5ff9\u5ffa\u5ffb\u5ffc\u5ffd\u5ffe\u5fff\u6000\u6001\u6002\u6003\u6004\u6005\u6006\u6007\u6008\u6009\u600a\u600b\u600c\u600d\u600e\u600f\u6010\u6011\u6012\u6013\u6014\u6015\u6016\u6017\u6018\u6019\u601a\u601b\u601c\u601d\u601e\u601f\u6020\u6021\u6022\u6023\u6024\u6025\u6026\u6027\u6028\u6029\u602a\u602b\u602c\u602d\u602e\u602f\u6030\u6031\u6032\u6033\u6034\u6035\u6036\u6037\u6038\u6039\u603a\u603b\u603c\u603d\u603e\u603f\u6040\u6041\u6042\u6043\u6044\u6045\u6046\u6047\u6048\u6049\u604a\u604b\u604c\u604d\u604e\u604f\u6050\u6051\u6052\u6053\u6054\u6055\u6056\u6057\u6058\u6059\u605a\u605b\u605c\u605d\u605e\u605f\u6060\u6061\u6062\u6063\u6064\u6065\u6066\u6067\u6068\u6069\u606a\u606b\u606c\u606d\u606e\u606f\u6070\u6071\u6072\u6073\u6074\u6075\u6076\u6077\u6078\u6079\u607a\u607b\u607c\u607d\u607e\u607f\u6080\u6081\u6082\u6083\u6084\u6085\u6086\u6087\u6088\u6089\u608a\u608b\u608c\u608d\u608e\u608f\u6090\u6091\u6092\u6093\u6094\u6095\u6096\u6097\u6098\u6099\u609a\u609b\u609c\u609d\u609e\u609f\u60a0\u60a1\u60a2\u60a3\u60a4\u60a5\u60a6\u60a7\u60a8\u60a9\u60aa\u60ab\u60ac\u60ad\u60ae\u60af\u60b0\u60b1\u60b2\u60b3\u60b4\u60b5\u60b6\u60b7\u60b8\u60b9\u60ba\u60bb\u60bc\u60bd\u60be\u60bf\u60c0\u60c1\u60c2\u60c3\u60c4\u60c5\u60c6\u60c7\u60c8\u60c9\u60ca\u60cb\u60cc\u60cd\u60ce\u60cf\u60d0\u60d1\u60d2\u60d3\u60d4\u60d5\u60d6\u60d7\u60d8\u60d9\u60da\u60db\u60dc\u60dd\u60de\u60df\u60e0\u60e1\u60e2\u60e3\u60e4\u60e5\u60e6\u60e7\u60e8\u60e9\u60ea\u60eb\u60ec\u60ed\u60ee\u60ef\u60f0\u60f1\u60f2\u60f3\u60f4\u60f5\u60f6\u60f7\u60f8\u60f9\u60fa\u60fb\u60fc\u60fd\u60fe\u60ff\u6100\u6101\u6102\u6103\u6104\u6105\u6106\u6107\u6108\u6109\u610a\u610b\u610c\u610d\u610e\u610f\u6110\u6111\u6112\u6113\u6114\u6115\u6116\u6117\u6118\u6119\u611a\u611b\u611c\u611d\u611e\u611f\u6120\u6121\u6122\u6123\u6124\u6125\u6126\u6127\u6128\u6129\u612a\u612b\u612c\u612d\u612e\u612f\u6130\u6131\u6132\u6133\u6134\u6135\u6136\u6137\u6138\u6139\u613a\u613b\u613c\u613d\u613e\u613f\u6140\u6141\u6142\u6143\u6144\u6145\u6146\u6147\u6148\u6149\u614a\u614b\u614c\u614d\u614e\u614f\u6150\u6151\u6152\u6153\u6154\u6155\u6156\u6157\u6158\u6159\u615a\u615b\u615c\u615d\u615e\u615f\u6160\u6161\u6162\u6163\u6164\u6165\u6166\u6167\u6168\u6169\u616a\u616b\u616c\u616d\u616e\u616f\u6170\u6171\u6172\u6173\u6174\u6175\u6176\u6177\u6178\u6179\u617a\u617b\u617c\u617d\u617e\u617f\u6180\u6181\u6182\u6183\u6184\u6185\u6186\u6187\u6188\u6189\u618a\u618b\u618c\u618d\u618e\u618f\u6190\u6191\u6192\u6193\u6194\u6195\u6196\u6197\u6198\u6199\u619a\u619b\u619c\u619d\u619e\u619f\u61a0\u61a1\u61a2\u61a3\u61a4\u61a5\u61a6\u61a7\u61a8\u61a9\u61aa\u61ab\u61ac\u61ad\u61ae\u61af\u61b0\u61b1\u61b2\u61b3\u61b4\u61b5\u61b6\u61b7\u61b8\u61b9\u61ba\u61bb\u61bc\u61bd\u61be\u61bf\u61c0\u61c1\u61c2\u61c3\u61c4\u61c5\u61c6\u61c7\u61c8\u61c9\u61ca\u61cb\u61cc\u61cd\u61ce\u61cf\u61d0\u61d1\u61d2\u61d3\u61d4\u61d5\u61d6\u61d7\u61d8\u61d9\u61da\u61db\u61dc\u61dd\u61de\u61df\u61e0\u61e1\u61e2\u61e3\u61e4\u61e5\u61e6\u61e7\u61e8\u61e9\u61ea\u61eb\u61ec\u61ed\u61ee\u61ef\u61f0\u61f1\u61f2\u61f3\u61f4\u61f5\u61f6\u61f7\u61f8\u61f9\u61fa\u61fb\u61fc\u61fd\u61fe\u61ff\u6200\u6201\u6202\u6203\u6204\u6205\u6206\u6207\u6208\u6209\u620a\u620b\u620c\u620d\u620e\u620f\u6210\u6211\u6212\u6213\u6214\u6215\u6216\u6217\u6218\u6219\u621a\u621b\u621c\u621d\u621e\u621f\u6220\u6221\u6222\u6223\u6224\u6225\u6226\u6227\u6228\u6229\u622a\u622b\u622c\u622d\u622e\u622f\u6230\u6231\u6232\u6233\u6234\u6235\u6236\u6237\u6238\u6239\u623a\u623b\u623c\u623d\u623e\u623f\u6240\u6241\u6242\u6243\u6244\u6245\u6246\u6247\u6248\u6249\u624a\u624b\u624c\u624d\u624e\u624f\u6250\u6251\u6252\u6253\u6254\u6255\u6256\u6257\u6258\u6259\u625a\u625b\u625c\u625d\u625e\u625f\u6260\u6261\u6262\u6263\u6264\u6265\u6266\u6267\u6268\u6269\u626a\u626b\u626c\u626d\u626e\u626f\u6270\u6271\u6272\u6273\u6274\u6275\u6276\u6277\u6278\u6279\u627a\u627b\u627c\u627d\u627e\u627f\u6280\u6281\u6282\u6283\u6284\u6285\u6286\u6287\u6288\u6289\u628a\u628b\u628c\u628d\u628e\u628f\u6290\u6291\u6292\u6293\u6294\u6295\u6296\u6297\u6298\u6299\u629a\u629b\u629c\u629d\u629e\u629f\u62a0\u62a1\u62a2\u62a3\u62a4\u62a5\u62a6\u62a7\u62a8\u62a9\u62aa\u62ab\u62ac\u62ad\u62ae\u62af\u62b0\u62b1\u62b2\u62b3\u62b4\u62b5\u62b6\u62b7\u62b8\u62b9\u62ba\u62bb\u62bc\u62bd\u62be\u62bf\u62c0\u62c1\u62c2\u62c3\u62c4\u62c5\u62c6\u62c7\u62c8\u62c9\u62ca\u62cb\u62cc\u62cd\u62ce\u62cf\u62d0\u62d1\u62d2\u62d3\u62d4\u62d5\u62d6\u62d7\u62d8\u62d9\u62da\u62db\u62dc\u62dd\u62de\u62df\u62e0\u62e1\u62e2\u62e3\u62e4\u62e5\u62e6\u62e7\u62e8\u62e9\u62ea\u62eb\u62ec\u62ed\u62ee\u62ef\u62f0\u62f1\u62f2\u62f3\u62f4\u62f5\u62f6\u62f7\u62f8\u62f9\u62fa\u62fb\u62fc\u62fd\u62fe\u62ff\u6300\u6301\u6302\u6303\u6304\u6305\u6306\u6307\u6308\u6309\u630a\u630b\u630c\u630d\u630e\u630f\u6310\u6311\u6312\u6313\u6314\u6315\u6316\u6317\u6318\u6319\u631a\u631b\u631c\u631d\u631e\u631f\u6320\u6321\u6322\u6323\u6324\u6325\u6326\u6327\u6328\u6329\u632a\u632b\u632c\u632d\u632e\u632f\u6330\u6331\u6332\u6333\u6334\u6335\u6336\u6337\u6338\u6339\u633a\u633b\u633c\u633d\u633e\u633f\u6340\u6341\u6342\u6343\u6344\u6345\u6346\u6347\u6348\u6349\u634a\u634b\u634c\u634d\u634e\u634f\u6350\u6351\u6352\u6353\u6354\u6355\u6356\u6357\u6358\u6359\u635a\u635b\u635c\u635d\u635e\u635f\u6360\u6361\u6362\u6363\u6364\u6365\u6366\u6367\u6368\u6369\u636a\u636b\u636c\u636d\u636e\u636f\u6370\u6371\u6372\u6373\u6374\u6375\u6376\u6377\u6378\u6379\u637a\u637b\u637c\u637d\u637e\u637f\u6380\u6381\u6382\u6383\u6384\u6385\u6386\u6387\u6388\u6389\u638a\u638b\u638c\u638d\u638e\u638f\u6390\u6391\u6392\u6393\u6394\u6395\u6396\u6397\u6398\u6399\u639a\u639b\u639c\u639d\u639e\u639f\u63a0\u63a1\u63a2\u63a3\u63a4\u63a5\u63a6\u63a7\u63a8\u63a9\u63aa\u63ab\u63ac\u63ad\u63ae\u63af\u63b0\u63b1\u63b2\u63b3\u63b4\u63b5\u63b6\u63b7\u63b8\u63b9\u63ba\u63bb\u63bc\u63bd\u63be\u63bf\u63c0\u63c1\u63c2\u63c3\u63c4\u63c5\u63c6\u63c7\u63c8\u63c9\u63ca\u63cb\u63cc\u63cd\u63ce\u63cf\u63d0\u63d1\u63d2\u63d3\u63d4\u63d5\u63d6\u63d7\u63d8\u63d9\u63da\u63db\u63dc\u63dd\u63de\u63df\u63e0\u63e1\u63e2\u63e3\u63e4\u63e5\u63e6\u63e7\u63e8\u63e9\u63ea\u63eb\u63ec\u63ed\u63ee\u63ef\u63f0\u63f1\u63f2\u63f3\u63f4\u63f5\u63f6\u63f7\u63f8\u63f9\u63fa\u63fb\u63fc\u63fd\u63fe\u63ff\u6400\u6401\u6402\u6403\u6404\u6405\u6406\u6407\u6408\u6409\u640a\u640b\u640c\u640d\u640e\u640f\u6410\u6411\u6412\u6413\u6414\u6415\u6416\u6417\u6418\u6419\u641a\u641b\u641c\u641d\u641e\u641f\u6420\u6421\u6422\u6423\u6424\u6425\u6426\u6427\u6428\u6429\u642a\u642b\u642c\u642d\u642e\u642f\u6430\u6431\u6432\u6433\u6434\u6435\u6436\u6437\u6438\u6439\u643a\u643b\u643c\u643d\u643e\u643f\u6440\u6441\u6442\u6443\u6444\u6445\u6446\u6447\u6448\u6449\u644a\u644b\u644c\u644d\u644e\u644f\u6450\u6451\u6452\u6453\u6454\u6455\u6456\u6457\u6458\u6459\u645a\u645b\u645c\u645d\u645e\u645f\u6460\u6461\u6462\u6463\u6464\u6465\u6466\u6467\u6468\u6469\u646a\u646b\u646c\u646d\u646e\u646f\u6470\u6471\u6472\u6473\u6474\u6475\u6476\u6477\u6478\u6479\u647a\u647b\u647c\u647d\u647e\u647f\u6480\u6481\u6482\u6483\u6484\u6485\u6486\u6487\u6488\u6489\u648a\u648b\u648c\u648d\u648e\u648f\u6490\u6491\u6492\u6493\u6494\u6495\u6496\u6497\u6498\u6499\u649a\u649b\u649c\u649d\u649e\u649f\u64a0\u64a1\u64a2\u64a3\u64a4\u64a5\u64a6\u64a7\u64a8\u64a9\u64aa\u64ab\u64ac\u64ad\u64ae\u64af\u64b0\u64b1\u64b2\u64b3\u64b4\u64b5\u64b6\u64b7\u64b8\u64b9\u64ba\u64bb\u64bc\u64bd\u64be\u64bf\u64c0\u64c1\u64c2\u64c3\u64c4\u64c5\u64c6\u64c7\u64c8\u64c9\u64ca\u64cb\u64cc\u64cd\u64ce\u64cf\u64d0\u64d1\u64d2\u64d3\u64d4\u64d5\u64d6\u64d7\u64d8\u64d9\u64da\u64db\u64dc\u64dd\u64de\u64df\u64e0\u64e1\u64e2\u64e3\u64e4\u64e5\u64e6\u64e7\u64e8\u64e9\u64ea\u64eb\u64ec\u64ed\u64ee\u64ef\u64f0\u64f1\u64f2\u64f3\u64f4\u64f5\u64f6\u64f7\u64f8\u64f9\u64fa\u64fb\u64fc\u64fd\u64fe\u64ff\u6500\u6501\u6502\u6503\u6504\u6505\u6506\u6507\u6508\u6509\u650a\u650b\u650c\u650d\u650e\u650f\u6510\u6511\u6512\u6513\u6514\u6515\u6516\u6517\u6518\u6519\u651a\u651b\u651c\u651d\u651e\u651f\u6520\u6521\u6522\u6523\u6524\u6525\u6526\u6527\u6528\u6529\u652a\u652b\u652c\u652d\u652e\u652f\u6530\u6531\u6532\u6533\u6534\u6535\u6536\u6537\u6538\u6539\u653a\u653b\u653c\u653d\u653e\u653f\u6540\u6541\u6542\u6543\u6544\u6545\u6546\u6547\u6548\u6549\u654a\u654b\u654c\u654d\u654e\u654f\u6550\u6551\u6552\u6553\u6554\u6555\u6556\u6557\u6558\u6559\u655a\u655b\u655c\u655d\u655e\u655f\u6560\u6561\u6562\u6563\u6564\u6565\u6566\u6567\u6568\u6569\u656a\u656b\u656c\u656d\u656e\u656f\u6570\u6571\u6572\u6573\u6574\u6575\u6576\u6577\u6578\u6579\u657a\u657b\u657c\u657d\u657e\u657f\u6580\u6581\u6582\u6583\u6584\u6585\u6586\u6587\u6588\u6589\u658a\u658b\u658c\u658d\u658e\u658f\u6590\u6591\u6592\u6593\u6594\u6595\u6596\u6597\u6598\u6599\u659a\u659b\u659c\u659d\u659e\u659f\u65a0\u65a1\u65a2\u65a3\u65a4\u65a5\u65a6\u65a7\u65a8\u65a9\u65aa\u65ab\u65ac\u65ad\u65ae\u65af\u65b0\u65b1\u65b2\u65b3\u65b4\u65b5\u65b6\u65b7\u65b8\u65b9\u65ba\u65bb\u65bc\u65bd\u65be\u65bf\u65c0\u65c1\u65c2\u65c3\u65c4\u65c5\u65c6\u65c7\u65c8\u65c9\u65ca\u65cb\u65cc\u65cd\u65ce\u65cf\u65d0\u65d1\u65d2\u65d3\u65d4\u65d5\u65d6\u65d7\u65d8\u65d9\u65da\u65db\u65dc\u65dd\u65de\u65df\u65e0\u65e1\u65e2\u65e3\u65e4\u65e5\u65e6\u65e7\u65e8\u65e9\u65ea\u65eb\u65ec\u65ed\u65ee\u65ef\u65f0\u65f1\u65f2\u65f3\u65f4\u65f5\u65f6\u65f7\u65f8\u65f9\u65fa\u65fb\u65fc\u65fd\u65fe\u65ff\u6600\u6601\u6602\u6603\u6604\u6605\u6606\u6607\u6608\u6609\u660a\u660b\u660c\u660d\u660e\u660f\u6610\u6611\u6612\u6613\u6614\u6615\u6616\u6617\u6618\u6619\u661a\u661b\u661c\u661d\u661e\u661f\u6620\u6621\u6622\u6623\u6624\u6625\u6626\u6627\u6628\u6629\u662a\u662b\u662c\u662d\u662e\u662f\u6630\u6631\u6632\u6633\u6634\u6635\u6636\u6637\u6638\u6639\u663a\u663b\u663c\u663d\u663e\u663f\u6640\u6641\u6642\u6643\u6644\u6645\u6646\u6647\u6648\u6649\u664a\u664b\u664c\u664d\u664e\u664f\u6650\u6651\u6652\u6653\u6654\u6655\u6656\u6657\u6658\u6659\u665a\u665b\u665c\u665d\u665e\u665f\u6660\u6661\u6662\u6663\u6664\u6665\u6666\u6667\u6668\u6669\u666a\u666b\u666c\u666d\u666e\u666f\u6670\u6671\u6672\u6673\u6674\u6675\u6676\u6677\u6678\u6679\u667a\u667b\u667c\u667d\u667e\u667f\u6680\u6681\u6682\u6683\u6684\u6685\u6686\u6687\u6688\u6689\u668a\u668b\u668c\u668d\u668e\u668f\u6690\u6691\u6692\u6693\u6694\u6695\u6696\u6697\u6698\u6699\u669a\u669b\u669c\u669d\u669e\u669f\u66a0\u66a1\u66a2\u66a3\u66a4\u66a5\u66a6\u66a7\u66a8\u66a9\u66aa\u66ab\u66ac\u66ad\u66ae\u66af\u66b0\u66b1\u66b2\u66b3\u66b4\u66b5\u66b6\u66b7\u66b8\u66b9\u66ba\u66bb\u66bc\u66bd\u66be\u66bf\u66c0\u66c1\u66c2\u66c3\u66c4\u66c5\u66c6\u66c7\u66c8\u66c9\u66ca\u66cb\u66cc\u66cd\u66ce\u66cf\u66d0\u66d1\u66d2\u66d3\u66d4\u66d5\u66d6\u66d7\u66d8\u66d9\u66da\u66db\u66dc\u66dd\u66de\u66df\u66e0\u66e1\u66e2\u66e3\u66e4\u66e5\u66e6\u66e7\u66e8\u66e9\u66ea\u66eb\u66ec\u66ed\u66ee\u66ef\u66f0\u66f1\u66f2\u66f3\u66f4\u66f5\u66f6\u66f7\u66f8\u66f9\u66fa\u66fb\u66fc\u66fd\u66fe\u66ff\u6700\u6701\u6702\u6703\u6704\u6705\u6706\u6707\u6708\u6709\u670a\u670b\u670c\u670d\u670e\u670f\u6710\u6711\u6712\u6713\u6714\u6715\u6716\u6717\u6718\u6719\u671a\u671b\u671c\u671d\u671e\u671f\u6720\u6721\u6722\u6723\u6724\u6725\u6726\u6727\u6728\u6729\u672a\u672b\u672c\u672d\u672e\u672f\u6730\u6731\u6732\u6733\u6734\u6735\u6736\u6737\u6738\u6739\u673a\u673b\u673c\u673d\u673e\u673f\u6740\u6741\u6742\u6743\u6744\u6745\u6746\u6747\u6748\u6749\u674a\u674b\u674c\u674d\u674e\u674f\u6750\u6751\u6752\u6753\u6754\u6755\u6756\u6757\u6758\u6759\u675a\u675b\u675c\u675d\u675e\u675f\u6760\u6761\u6762\u6763\u6764\u6765\u6766\u6767\u6768\u6769\u676a\u676b\u676c\u676d\u676e\u676f\u6770\u6771\u6772\u6773\u6774\u6775\u6776\u6777\u6778\u6779\u677a\u677b\u677c\u677d\u677e\u677f\u6780\u6781\u6782\u6783\u6784\u6785\u6786\u6787\u6788\u6789\u678a\u678b\u678c\u678d\u678e\u678f\u6790\u6791\u6792\u6793\u6794\u6795\u6796\u6797\u6798\u6799\u679a\u679b\u679c\u679d\u679e\u679f\u67a0\u67a1\u67a2\u67a3\u67a4\u67a5\u67a6\u67a7\u67a8\u67a9\u67aa\u67ab\u67ac\u67ad\u67ae\u67af\u67b0\u67b1\u67b2\u67b3\u67b4\u67b5\u67b6\u67b7\u67b8\u67b9\u67ba\u67bb\u67bc\u67bd\u67be\u67bf\u67c0\u67c1\u67c2\u67c3\u67c4\u67c5\u67c6\u67c7\u67c8\u67c9\u67ca\u67cb\u67cc\u67cd\u67ce\u67cf\u67d0\u67d1\u67d2\u67d3\u67d4\u67d5\u67d6\u67d7\u67d8\u67d9\u67da\u67db\u67dc\u67dd\u67de\u67df\u67e0\u67e1\u67e2\u67e3\u67e4\u67e5\u67e6\u67e7\u67e8\u67e9\u67ea\u67eb\u67ec\u67ed\u67ee\u67ef\u67f0\u67f1\u67f2\u67f3\u67f4\u67f5\u67f6\u67f7\u67f8\u67f9\u67fa\u67fb\u67fc\u67fd\u67fe\u67ff\u6800\u6801\u6802\u6803\u6804\u6805\u6806\u6807\u6808\u6809\u680a\u680b\u680c\u680d\u680e\u680f\u6810\u6811\u6812\u6813\u6814\u6815\u6816\u6817\u6818\u6819\u681a\u681b\u681c\u681d\u681e\u681f\u6820\u6821\u6822\u6823\u6824\u6825\u6826\u6827\u6828\u6829\u682a\u682b\u682c\u682d\u682e\u682f\u6830\u6831\u6832\u6833\u6834\u6835\u6836\u6837\u6838\u6839\u683a\u683b\u683c\u683d\u683e\u683f\u6840\u6841\u6842\u6843\u6844\u6845\u6846\u6847\u6848\u6849\u684a\u684b\u684c\u684d\u684e\u684f\u6850\u6851\u6852\u6853\u6854\u6855\u6856\u6857\u6858\u6859\u685a\u685b\u685c\u685d\u685e\u685f\u6860\u6861\u6862\u6863\u6864\u6865\u6866\u6867\u6868\u6869\u686a\u686b\u686c\u686d\u686e\u686f\u6870\u6871\u6872\u6873\u6874\u6875\u6876\u6877\u6878\u6879\u687a\u687b\u687c\u687d\u687e\u687f\u6880\u6881\u6882\u6883\u6884\u6885\u6886\u6887\u6888\u6889\u688a\u688b\u688c\u688d\u688e\u688f\u6890\u6891\u6892\u6893\u6894\u6895\u6896\u6897\u6898\u6899\u689a\u689b\u689c\u689d\u689e\u689f\u68a0\u68a1\u68a2\u68a3\u68a4\u68a5\u68a6\u68a7\u68a8\u68a9\u68aa\u68ab\u68ac\u68ad\u68ae\u68af\u68b0\u68b1\u68b2\u68b3\u68b4\u68b5\u68b6\u68b7\u68b8\u68b9\u68ba\u68bb\u68bc\u68bd\u68be\u68bf\u68c0\u68c1\u68c2\u68c3\u68c4\u68c5\u68c6\u68c7\u68c8\u68c9\u68ca\u68cb\u68cc\u68cd\u68ce\u68cf\u68d0\u68d1\u68d2\u68d3\u68d4\u68d5\u68d6\u68d7\u68d8\u68d9\u68da\u68db\u68dc\u68dd\u68de\u68df\u68e0\u68e1\u68e2\u68e3\u68e4\u68e5\u68e6\u68e7\u68e8\u68e9\u68ea\u68eb\u68ec\u68ed\u68ee\u68ef\u68f0\u68f1\u68f2\u68f3\u68f4\u68f5\u68f6\u68f7\u68f8\u68f9\u68fa\u68fb\u68fc\u68fd\u68fe\u68ff\u6900\u6901\u6902\u6903\u6904\u6905\u6906\u6907\u6908\u6909\u690a\u690b\u690c\u690d\u690e\u690f\u6910\u6911\u6912\u6913\u6914\u6915\u6916\u6917\u6918\u6919\u691a\u691b\u691c\u691d\u691e\u691f\u6920\u6921\u6922\u6923\u6924\u6925\u6926\u6927\u6928\u6929\u692a\u692b\u692c\u692d\u692e\u692f\u6930\u6931\u6932\u6933\u6934\u6935\u6936\u6937\u6938\u6939\u693a\u693b\u693c\u693d\u693e\u693f\u6940\u6941\u6942\u6943\u6944\u6945\u6946\u6947\u6948\u6949\u694a\u694b\u694c\u694d\u694e\u694f\u6950\u6951\u6952\u6953\u6954\u6955\u6956\u6957\u6958\u6959\u695a\u695b\u695c\u695d\u695e\u695f\u6960\u6961\u6962\u6963\u6964\u6965\u6966\u6967\u6968\u6969\u696a\u696b\u696c\u696d\u696e\u696f\u6970\u6971\u6972\u6973\u6974\u6975\u6976\u6977\u6978\u6979\u697a\u697b\u697c\u697d\u697e\u697f\u6980\u6981\u6982\u6983\u6984\u6985\u6986\u6987\u6988\u6989\u698a\u698b\u698c\u698d\u698e\u698f\u6990\u6991\u6992\u6993\u6994\u6995\u6996\u6997\u6998\u6999\u699a\u699b\u699c\u699d\u699e\u699f\u69a0\u69a1\u69a2\u69a3\u69a4\u69a5\u69a6\u69a7\u69a8\u69a9\u69aa\u69ab\u69ac\u69ad\u69ae\u69af\u69b0\u69b1\u69b2\u69b3\u69b4\u69b5\u69b6\u69b7\u69b8\u69b9\u69ba\u69bb\u69bc\u69bd\u69be\u69bf\u69c0\u69c1\u69c2\u69c3\u69c4\u69c5\u69c6\u69c7\u69c8\u69c9\u69ca\u69cb\u69cc\u69cd\u69ce\u69cf\u69d0\u69d1\u69d2\u69d3\u69d4\u69d5\u69d6\u69d7\u69d8\u69d9\u69da\u69db\u69dc\u69dd\u69de\u69df\u69e0\u69e1\u69e2\u69e3\u69e4\u69e5\u69e6\u69e7\u69e8\u69e9\u69ea\u69eb\u69ec\u69ed\u69ee\u69ef\u69f0\u69f1\u69f2\u69f3\u69f4\u69f5\u69f6\u69f7\u69f8\u69f9\u69fa\u69fb\u69fc\u69fd\u69fe\u69ff\u6a00\u6a01\u6a02\u6a03\u6a04\u6a05\u6a06\u6a07\u6a08\u6a09\u6a0a\u6a0b\u6a0c\u6a0d\u6a0e\u6a0f\u6a10\u6a11\u6a12\u6a13\u6a14\u6a15\u6a16\u6a17\u6a18\u6a19\u6a1a\u6a1b\u6a1c\u6a1d\u6a1e\u6a1f\u6a20\u6a21\u6a22\u6a23\u6a24\u6a25\u6a26\u6a27\u6a28\u6a29\u6a2a\u6a2b\u6a2c\u6a2d\u6a2e\u6a2f\u6a30\u6a31\u6a32\u6a33\u6a34\u6a35\u6a36\u6a37\u6a38\u6a39\u6a3a\u6a3b\u6a3c\u6a3d\u6a3e\u6a3f\u6a40\u6a41\u6a42\u6a43\u6a44\u6a45\u6a46\u6a47\u6a48\u6a49\u6a4a\u6a4b\u6a4c\u6a4d\u6a4e\u6a4f\u6a50\u6a51\u6a52\u6a53\u6a54\u6a55\u6a56\u6a57\u6a58\u6a59\u6a5a\u6a5b\u6a5c\u6a5d\u6a5e\u6a5f\u6a60\u6a61\u6a62\u6a63\u6a64\u6a65\u6a66\u6a67\u6a68\u6a69\u6a6a\u6a6b\u6a6c\u6a6d\u6a6e\u6a6f\u6a70\u6a71\u6a72\u6a73\u6a74\u6a75\u6a76\u6a77\u6a78\u6a79\u6a7a\u6a7b\u6a7c\u6a7d\u6a7e\u6a7f\u6a80\u6a81\u6a82\u6a83\u6a84\u6a85\u6a86\u6a87\u6a88\u6a89\u6a8a\u6a8b\u6a8c\u6a8d\u6a8e\u6a8f\u6a90\u6a91\u6a92\u6a93\u6a94\u6a95\u6a96\u6a97\u6a98\u6a99\u6a9a\u6a9b\u6a9c\u6a9d\u6a9e\u6a9f\u6aa0\u6aa1\u6aa2\u6aa3\u6aa4\u6aa5\u6aa6\u6aa7\u6aa8\u6aa9\u6aaa\u6aab\u6aac\u6aad\u6aae\u6aaf\u6ab0\u6ab1\u6ab2\u6ab3\u6ab4\u6ab5\u6ab6\u6ab7\u6ab8\u6ab9\u6aba\u6abb\u6abc\u6abd\u6abe\u6abf\u6ac0\u6ac1\u6ac2\u6ac3\u6ac4\u6ac5\u6ac6\u6ac7\u6ac8\u6ac9\u6aca\u6acb\u6acc\u6acd\u6ace\u6acf\u6ad0\u6ad1\u6ad2\u6ad3\u6ad4\u6ad5\u6ad6\u6ad7\u6ad8\u6ad9\u6ada\u6adb\u6adc\u6add\u6ade\u6adf\u6ae0\u6ae1\u6ae2\u6ae3\u6ae4\u6ae5\u6ae6\u6ae7\u6ae8\u6ae9\u6aea\u6aeb\u6aec\u6aed\u6aee\u6aef\u6af0\u6af1\u6af2\u6af3\u6af4\u6af5\u6af6\u6af7\u6af8\u6af9\u6afa\u6afb\u6afc\u6afd\u6afe\u6aff\u6b00\u6b01\u6b02\u6b03\u6b04\u6b05\u6b06\u6b07\u6b08\u6b09\u6b0a\u6b0b\u6b0c\u6b0d\u6b0e\u6b0f\u6b10\u6b11\u6b12\u6b13\u6b14\u6b15\u6b16\u6b17\u6b18\u6b19\u6b1a\u6b1b\u6b1c\u6b1d\u6b1e\u6b1f\u6b20\u6b21\u6b22\u6b23\u6b24\u6b25\u6b26\u6b27\u6b28\u6b29\u6b2a\u6b2b\u6b2c\u6b2d\u6b2e\u6b2f\u6b30\u6b31\u6b32\u6b33\u6b34\u6b35\u6b36\u6b37\u6b38\u6b39\u6b3a\u6b3b\u6b3c\u6b3d\u6b3e\u6b3f\u6b40\u6b41\u6b42\u6b43\u6b44\u6b45\u6b46\u6b47\u6b48\u6b49\u6b4a\u6b4b\u6b4c\u6b4d\u6b4e\u6b4f\u6b50\u6b51\u6b52\u6b53\u6b54\u6b55\u6b56\u6b57\u6b58\u6b59\u6b5a\u6b5b\u6b5c\u6b5d\u6b5e\u6b5f\u6b60\u6b61\u6b62\u6b63\u6b64\u6b65\u6b66\u6b67\u6b68\u6b69\u6b6a\u6b6b\u6b6c\u6b6d\u6b6e\u6b6f\u6b70\u6b71\u6b72\u6b73\u6b74\u6b75\u6b76\u6b77\u6b78\u6b79\u6b7a\u6b7b\u6b7c\u6b7d\u6b7e\u6b7f\u6b80\u6b81\u6b82\u6b83\u6b84\u6b85\u6b86\u6b87\u6b88\u6b89\u6b8a\u6b8b\u6b8c\u6b8d\u6b8e\u6b8f\u6b90\u6b91\u6b92\u6b93\u6b94\u6b95\u6b96\u6b97\u6b98\u6b99\u6b9a\u6b9b\u6b9c\u6b9d\u6b9e\u6b9f\u6ba0\u6ba1\u6ba2\u6ba3\u6ba4\u6ba5\u6ba6\u6ba7\u6ba8\u6ba9\u6baa\u6bab\u6bac\u6bad\u6bae\u6baf\u6bb0\u6bb1\u6bb2\u6bb3\u6bb4\u6bb5\u6bb6\u6bb7\u6bb8\u6bb9\u6bba\u6bbb\u6bbc\u6bbd\u6bbe\u6bbf\u6bc0\u6bc1\u6bc2\u6bc3\u6bc4\u6bc5\u6bc6\u6bc7\u6bc8\u6bc9\u6bca\u6bcb\u6bcc\u6bcd\u6bce\u6bcf\u6bd0\u6bd1\u6bd2\u6bd3\u6bd4\u6bd5\u6bd6\u6bd7\u6bd8\u6bd9\u6bda\u6bdb\u6bdc\u6bdd\u6bde\u6bdf\u6be0\u6be1\u6be2\u6be3\u6be4\u6be5\u6be6\u6be7\u6be8\u6be9\u6bea\u6beb\u6bec\u6bed\u6bee\u6bef\u6bf0\u6bf1\u6bf2\u6bf3\u6bf4\u6bf5\u6bf6\u6bf7\u6bf8\u6bf9\u6bfa\u6bfb\u6bfc\u6bfd\u6bfe\u6bff\u6c00\u6c01\u6c02\u6c03\u6c04\u6c05\u6c06\u6c07\u6c08\u6c09\u6c0a\u6c0b\u6c0c\u6c0d\u6c0e\u6c0f\u6c10\u6c11\u6c12\u6c13\u6c14\u6c15\u6c16\u6c17\u6c18\u6c19\u6c1a\u6c1b\u6c1c\u6c1d\u6c1e\u6c1f\u6c20\u6c21\u6c22\u6c23\u6c24\u6c25\u6c26\u6c27\u6c28\u6c29\u6c2a\u6c2b\u6c2c\u6c2d\u6c2e\u6c2f\u6c30\u6c31\u6c32\u6c33\u6c34\u6c35\u6c36\u6c37\u6c38\u6c39\u6c3a\u6c3b\u6c3c\u6c3d\u6c3e\u6c3f\u6c40\u6c41\u6c42\u6c43\u6c44\u6c45\u6c46\u6c47\u6c48\u6c49\u6c4a\u6c4b\u6c4c\u6c4d\u6c4e\u6c4f\u6c50\u6c51\u6c52\u6c53\u6c54\u6c55\u6c56\u6c57\u6c58\u6c59\u6c5a\u6c5b\u6c5c\u6c5d\u6c5e\u6c5f\u6c60\u6c61\u6c62\u6c63\u6c64\u6c65\u6c66\u6c67\u6c68\u6c69\u6c6a\u6c6b\u6c6c\u6c6d\u6c6e\u6c6f\u6c70\u6c71\u6c72\u6c73\u6c74\u6c75\u6c76\u6c77\u6c78\u6c79\u6c7a\u6c7b\u6c7c\u6c7d\u6c7e\u6c7f\u6c80\u6c81\u6c82\u6c83\u6c84\u6c85\u6c86\u6c87\u6c88\u6c89\u6c8a\u6c8b\u6c8c\u6c8d\u6c8e\u6c8f\u6c90\u6c91\u6c92\u6c93\u6c94\u6c95\u6c96\u6c97\u6c98\u6c99\u6c9a\u6c9b\u6c9c\u6c9d\u6c9e\u6c9f\u6ca0\u6ca1\u6ca2\u6ca3\u6ca4\u6ca5\u6ca6\u6ca7\u6ca8\u6ca9\u6caa\u6cab\u6cac\u6cad\u6cae\u6caf\u6cb0\u6cb1\u6cb2\u6cb3\u6cb4\u6cb5\u6cb6\u6cb7\u6cb8\u6cb9\u6cba\u6cbb\u6cbc\u6cbd\u6cbe\u6cbf\u6cc0\u6cc1\u6cc2\u6cc3\u6cc4\u6cc5\u6cc6\u6cc7\u6cc8\u6cc9\u6cca\u6ccb\u6ccc\u6ccd\u6cce\u6ccf\u6cd0\u6cd1\u6cd2\u6cd3\u6cd4\u6cd5\u6cd6\u6cd7\u6cd8\u6cd9\u6cda\u6cdb\u6cdc\u6cdd\u6cde\u6cdf\u6ce0\u6ce1\u6ce2\u6ce3\u6ce4\u6ce5\u6ce6\u6ce7\u6ce8\u6ce9\u6cea\u6ceb\u6cec\u6ced\u6cee\u6cef\u6cf0\u6cf1\u6cf2\u6cf3\u6cf4\u6cf5\u6cf6\u6cf7\u6cf8\u6cf9\u6cfa\u6cfb\u6cfc\u6cfd\u6cfe\u6cff\u6d00\u6d01\u6d02\u6d03\u6d04\u6d05\u6d06\u6d07\u6d08\u6d09\u6d0a\u6d0b\u6d0c\u6d0d\u6d0e\u6d0f\u6d10\u6d11\u6d12\u6d13\u6d14\u6d15\u6d16\u6d17\u6d18\u6d19\u6d1a\u6d1b\u6d1c\u6d1d\u6d1e\u6d1f\u6d20\u6d21\u6d22\u6d23\u6d24\u6d25\u6d26\u6d27\u6d28\u6d29\u6d2a\u6d2b\u6d2c\u6d2d\u6d2e\u6d2f\u6d30\u6d31\u6d32\u6d33\u6d34\u6d35\u6d36\u6d37\u6d38\u6d39\u6d3a\u6d3b\u6d3c\u6d3d\u6d3e\u6d3f\u6d40\u6d41\u6d42\u6d43\u6d44\u6d45\u6d46\u6d47\u6d48\u6d49\u6d4a\u6d4b\u6d4c\u6d4d\u6d4e\u6d4f\u6d50\u6d51\u6d52\u6d53\u6d54\u6d55\u6d56\u6d57\u6d58\u6d59\u6d5a\u6d5b\u6d5c\u6d5d\u6d5e\u6d5f\u6d60\u6d61\u6d62\u6d63\u6d64\u6d65\u6d66\u6d67\u6d68\u6d69\u6d6a\u6d6b\u6d6c\u6d6d\u6d6e\u6d6f\u6d70\u6d71\u6d72\u6d73\u6d74\u6d75\u6d76\u6d77\u6d78\u6d79\u6d7a\u6d7b\u6d7c\u6d7d\u6d7e\u6d7f\u6d80\u6d81\u6d82\u6d83\u6d84\u6d85\u6d86\u6d87\u6d88\u6d89\u6d8a\u6d8b\u6d8c\u6d8d\u6d8e\u6d8f\u6d90\u6d91\u6d92\u6d93\u6d94\u6d95\u6d96\u6d97\u6d98\u6d99\u6d9a\u6d9b\u6d9c\u6d9d\u6d9e\u6d9f\u6da0\u6da1\u6da2\u6da3\u6da4\u6da5\u6da6\u6da7\u6da8\u6da9\u6daa\u6dab\u6dac\u6dad\u6dae\u6daf\u6db0\u6db1\u6db2\u6db3\u6db4\u6db5\u6db6\u6db7\u6db8\u6db9\u6dba\u6dbb\u6dbc\u6dbd\u6dbe\u6dbf\u6dc0\u6dc1\u6dc2\u6dc3\u6dc4\u6dc5\u6dc6\u6dc7\u6dc8\u6dc9\u6dca\u6dcb\u6dcc\u6dcd\u6dce\u6dcf\u6dd0\u6dd1\u6dd2\u6dd3\u6dd4\u6dd5\u6dd6\u6dd7\u6dd8\u6dd9\u6dda\u6ddb\u6ddc\u6ddd\u6dde\u6ddf\u6de0\u6de1\u6de2\u6de3\u6de4\u6de5\u6de6\u6de7\u6de8\u6de9\u6dea\u6deb\u6dec\u6ded\u6dee\u6def\u6df0\u6df1\u6df2\u6df3\u6df4\u6df5\u6df6\u6df7\u6df8\u6df9\u6dfa\u6dfb\u6dfc\u6dfd\u6dfe\u6dff\u6e00\u6e01\u6e02\u6e03\u6e04\u6e05\u6e06\u6e07\u6e08\u6e09\u6e0a\u6e0b\u6e0c\u6e0d\u6e0e\u6e0f\u6e10\u6e11\u6e12\u6e13\u6e14\u6e15\u6e16\u6e17\u6e18\u6e19\u6e1a\u6e1b\u6e1c\u6e1d\u6e1e\u6e1f\u6e20\u6e21\u6e22\u6e23\u6e24\u6e25\u6e26\u6e27\u6e28\u6e29\u6e2a\u6e2b\u6e2c\u6e2d\u6e2e\u6e2f\u6e30\u6e31\u6e32\u6e33\u6e34\u6e35\u6e36\u6e37\u6e38\u6e39\u6e3a\u6e3b\u6e3c\u6e3d\u6e3e\u6e3f\u6e40\u6e41\u6e42\u6e43\u6e44\u6e45\u6e46\u6e47\u6e48\u6e49\u6e4a\u6e4b\u6e4c\u6e4d\u6e4e\u6e4f\u6e50\u6e51\u6e52\u6e53\u6e54\u6e55\u6e56\u6e57\u6e58\u6e59\u6e5a\u6e5b\u6e5c\u6e5d\u6e5e\u6e5f\u6e60\u6e61\u6e62\u6e63\u6e64\u6e65\u6e66\u6e67\u6e68\u6e69\u6e6a\u6e6b\u6e6c\u6e6d\u6e6e\u6e6f\u6e70\u6e71\u6e72\u6e73\u6e74\u6e75\u6e76\u6e77\u6e78\u6e79\u6e7a\u6e7b\u6e7c\u6e7d\u6e7e\u6e7f\u6e80\u6e81\u6e82\u6e83\u6e84\u6e85\u6e86\u6e87\u6e88\u6e89\u6e8a\u6e8b\u6e8c\u6e8d\u6e8e\u6e8f\u6e90\u6e91\u6e92\u6e93\u6e94\u6e95\u6e96\u6e97\u6e98\u6e99\u6e9a\u6e9b\u6e9c\u6e9d\u6e9e\u6e9f\u6ea0\u6ea1\u6ea2\u6ea3\u6ea4\u6ea5\u6ea6\u6ea7\u6ea8\u6ea9\u6eaa\u6eab\u6eac\u6ead\u6eae\u6eaf\u6eb0\u6eb1\u6eb2\u6eb3\u6eb4\u6eb5\u6eb6\u6eb7\u6eb8\u6eb9\u6eba\u6ebb\u6ebc\u6ebd\u6ebe\u6ebf\u6ec0\u6ec1\u6ec2\u6ec3\u6ec4\u6ec5\u6ec6\u6ec7\u6ec8\u6ec9\u6eca\u6ecb\u6ecc\u6ecd\u6ece\u6ecf\u6ed0\u6ed1\u6ed2\u6ed3\u6ed4\u6ed5\u6ed6\u6ed7\u6ed8\u6ed9\u6eda\u6edb\u6edc\u6edd\u6ede\u6edf\u6ee0\u6ee1\u6ee2\u6ee3\u6ee4\u6ee5\u6ee6\u6ee7\u6ee8\u6ee9\u6eea\u6eeb\u6eec\u6eed\u6eee\u6eef\u6ef0\u6ef1\u6ef2\u6ef3\u6ef4\u6ef5\u6ef6\u6ef7\u6ef8\u6ef9\u6efa\u6efb\u6efc\u6efd\u6efe\u6eff\u6f00\u6f01\u6f02\u6f03\u6f04\u6f05\u6f06\u6f07\u6f08\u6f09\u6f0a\u6f0b\u6f0c\u6f0d\u6f0e\u6f0f\u6f10\u6f11\u6f12\u6f13\u6f14\u6f15\u6f16\u6f17\u6f18\u6f19\u6f1a\u6f1b\u6f1c\u6f1d\u6f1e\u6f1f\u6f20\u6f21\u6f22\u6f23\u6f24\u6f25\u6f26\u6f27\u6f28\u6f29\u6f2a\u6f2b\u6f2c\u6f2d\u6f2e\u6f2f\u6f30\u6f31\u6f32\u6f33\u6f34\u6f35\u6f36\u6f37\u6f38\u6f39\u6f3a\u6f3b\u6f3c\u6f3d\u6f3e\u6f3f\u6f40\u6f41\u6f42\u6f43\u6f44\u6f45\u6f46\u6f47\u6f48\u6f49\u6f4a\u6f4b\u6f4c\u6f4d\u6f4e\u6f4f\u6f50\u6f51\u6f52\u6f53\u6f54\u6f55\u6f56\u6f57\u6f58\u6f59\u6f5a\u6f5b\u6f5c\u6f5d\u6f5e\u6f5f\u6f60\u6f61\u6f62\u6f63\u6f64\u6f65\u6f66\u6f67\u6f68\u6f69\u6f6a\u6f6b\u6f6c\u6f6d\u6f6e\u6f6f\u6f70\u6f71\u6f72\u6f73\u6f74\u6f75\u6f76\u6f77\u6f78\u6f79\u6f7a\u6f7b\u6f7c\u6f7d\u6f7e\u6f7f\u6f80\u6f81\u6f82\u6f83\u6f84\u6f85\u6f86\u6f87\u6f88\u6f89\u6f8a\u6f8b\u6f8c\u6f8d\u6f8e\u6f8f\u6f90\u6f91\u6f92\u6f93\u6f94\u6f95\u6f96\u6f97\u6f98\u6f99\u6f9a\u6f9b\u6f9c\u6f9d\u6f9e\u6f9f\u6fa0\u6fa1\u6fa2\u6fa3\u6fa4\u6fa5\u6fa6\u6fa7\u6fa8\u6fa9\u6faa\u6fab\u6fac\u6fad\u6fae\u6faf\u6fb0\u6fb1\u6fb2\u6fb3\u6fb4\u6fb5\u6fb6\u6fb7\u6fb8\u6fb9\u6fba\u6fbb\u6fbc\u6fbd\u6fbe\u6fbf\u6fc0\u6fc1\u6fc2\u6fc3\u6fc4\u6fc5\u6fc6\u6fc7\u6fc8\u6fc9\u6fca\u6fcb\u6fcc\u6fcd\u6fce\u6fcf\u6fd0\u6fd1\u6fd2\u6fd3\u6fd4\u6fd5\u6fd6\u6fd7\u6fd8\u6fd9\u6fda\u6fdb\u6fdc\u6fdd\u6fde\u6fdf\u6fe0\u6fe1\u6fe2\u6fe3\u6fe4\u6fe5\u6fe6\u6fe7\u6fe8\u6fe9\u6fea\u6feb\u6fec\u6fed\u6fee\u6fef\u6ff0\u6ff1\u6ff2\u6ff3\u6ff4\u6ff5\u6ff6\u6ff7\u6ff8\u6ff9\u6ffa\u6ffb\u6ffc\u6ffd\u6ffe\u6fff\u7000\u7001\u7002\u7003\u7004\u7005\u7006\u7007\u7008\u7009\u700a\u700b\u700c\u700d\u700e\u700f\u7010\u7011\u7012\u7013\u7014\u7015\u7016\u7017\u7018\u7019\u701a\u701b\u701c\u701d\u701e\u701f\u7020\u7021\u7022\u7023\u7024\u7025\u7026\u7027\u7028\u7029\u702a\u702b\u702c\u702d\u702e\u702f\u7030\u7031\u7032\u7033\u7034\u7035\u7036\u7037\u7038\u7039\u703a\u703b\u703c\u703d\u703e\u703f\u7040\u7041\u7042\u7043\u7044\u7045\u7046\u7047\u7048\u7049\u704a\u704b\u704c\u704d\u704e\u704f\u7050\u7051\u7052\u7053\u7054\u7055\u7056\u7057\u7058\u7059\u705a\u705b\u705c\u705d\u705e\u705f\u7060\u7061\u7062\u7063\u7064\u7065\u7066\u7067\u7068\u7069\u706a\u706b\u706c\u706d\u706e\u706f\u7070\u7071\u7072\u7073\u7074\u7075\u7076\u7077\u7078\u7079\u707a\u707b\u707c\u707d\u707e\u707f\u7080\u7081\u7082\u7083\u7084\u7085\u7086\u7087\u7088\u7089\u708a\u708b\u708c\u708d\u708e\u708f\u7090\u7091\u7092\u7093\u7094\u7095\u7096\u7097\u7098\u7099\u709a\u709b\u709c\u709d\u709e\u709f\u70a0\u70a1\u70a2\u70a3\u70a4\u70a5\u70a6\u70a7\u70a8\u70a9\u70aa\u70ab\u70ac\u70ad\u70ae\u70af\u70b0\u70b1\u70b2\u70b3\u70b4\u70b5\u70b6\u70b7\u70b8\u70b9\u70ba\u70bb\u70bc\u70bd\u70be\u70bf\u70c0\u70c1\u70c2\u70c3\u70c4\u70c5\u70c6\u70c7\u70c8\u70c9\u70ca\u70cb\u70cc\u70cd\u70ce\u70cf\u70d0\u70d1\u70d2\u70d3\u70d4\u70d5\u70d6\u70d7\u70d8\u70d9\u70da\u70db\u70dc\u70dd\u70de\u70df\u70e0\u70e1\u70e2\u70e3\u70e4\u70e5\u70e6\u70e7\u70e8\u70e9\u70ea\u70eb\u70ec\u70ed\u70ee\u70ef\u70f0\u70f1\u70f2\u70f3\u70f4\u70f5\u70f6\u70f7\u70f8\u70f9\u70fa\u70fb\u70fc\u70fd\u70fe\u70ff\u7100\u7101\u7102\u7103\u7104\u7105\u7106\u7107\u7108\u7109\u710a\u710b\u710c\u710d\u710e\u710f\u7110\u7111\u7112\u7113\u7114\u7115\u7116\u7117\u7118\u7119\u711a\u711b\u711c\u711d\u711e\u711f\u7120\u7121\u7122\u7123\u7124\u7125\u7126\u7127\u7128\u7129\u712a\u712b\u712c\u712d\u712e\u712f\u7130\u7131\u7132\u7133\u7134\u7135\u7136\u7137\u7138\u7139\u713a\u713b\u713c\u713d\u713e\u713f\u7140\u7141\u7142\u7143\u7144\u7145\u7146\u7147\u7148\u7149\u714a\u714b\u714c\u714d\u714e\u714f\u7150\u7151\u7152\u7153\u7154\u7155\u7156\u7157\u7158\u7159\u715a\u715b\u715c\u715d\u715e\u715f\u7160\u7161\u7162\u7163\u7164\u7165\u7166\u7167\u7168\u7169\u716a\u716b\u716c\u716d\u716e\u716f\u7170\u7171\u7172\u7173\u7174\u7175\u7176\u7177\u7178\u7179\u717a\u717b\u717c\u717d\u717e\u717f\u7180\u7181\u7182\u7183\u7184\u7185\u7186\u7187\u7188\u7189\u718a\u718b\u718c\u718d\u718e\u718f\u7190\u7191\u7192\u7193\u7194\u7195\u7196\u7197\u7198\u7199\u719a\u719b\u719c\u719d\u719e\u719f\u71a0\u71a1\u71a2\u71a3\u71a4\u71a5\u71a6\u71a7\u71a8\u71a9\u71aa\u71ab\u71ac\u71ad\u71ae\u71af\u71b0\u71b1\u71b2\u71b3\u71b4\u71b5\u71b6\u71b7\u71b8\u71b9\u71ba\u71bb\u71bc\u71bd\u71be\u71bf\u71c0\u71c1\u71c2\u71c3\u71c4\u71c5\u71c6\u71c7\u71c8\u71c9\u71ca\u71cb\u71cc\u71cd\u71ce\u71cf\u71d0\u71d1\u71d2\u71d3\u71d4\u71d5\u71d6\u71d7\u71d8\u71d9\u71da\u71db\u71dc\u71dd\u71de\u71df\u71e0\u71e1\u71e2\u71e3\u71e4\u71e5\u71e6\u71e7\u71e8\u71e9\u71ea\u71eb\u71ec\u71ed\u71ee\u71ef\u71f0\u71f1\u71f2\u71f3\u71f4\u71f5\u71f6\u71f7\u71f8\u71f9\u71fa\u71fb\u71fc\u71fd\u71fe\u71ff\u7200\u7201\u7202\u7203\u7204\u7205\u7206\u7207\u7208\u7209\u720a\u720b\u720c\u720d\u720e\u720f\u7210\u7211\u7212\u7213\u7214\u7215\u7216\u7217\u7218\u7219\u721a\u721b\u721c\u721d\u721e\u721f\u7220\u7221\u7222\u7223\u7224\u7225\u7226\u7227\u7228\u7229\u722a\u722b\u722c\u722d\u722e\u722f\u7230\u7231\u7232\u7233\u7234\u7235\u7236\u7237\u7238\u7239\u723a\u723b\u723c\u723d\u723e\u723f\u7240\u7241\u7242\u7243\u7244\u7245\u7246\u7247\u7248\u7249\u724a\u724b\u724c\u724d\u724e\u724f\u7250\u7251\u7252\u7253\u7254\u7255\u7256\u7257\u7258\u7259\u725a\u725b\u725c\u725d\u725e\u725f\u7260\u7261\u7262\u7263\u7264\u7265\u7266\u7267\u7268\u7269\u726a\u726b\u726c\u726d\u726e\u726f\u7270\u7271\u7272\u7273\u7274\u7275\u7276\u7277\u7278\u7279\u727a\u727b\u727c\u727d\u727e\u727f\u7280\u7281\u7282\u7283\u7284\u7285\u7286\u7287\u7288\u7289\u728a\u728b\u728c\u728d\u728e\u728f\u7290\u7291\u7292\u7293\u7294\u7295\u7296\u7297\u7298\u7299\u729a\u729b\u729c\u729d\u729e\u729f\u72a0\u72a1\u72a2\u72a3\u72a4\u72a5\u72a6\u72a7\u72a8\u72a9\u72aa\u72ab\u72ac\u72ad\u72ae\u72af\u72b0\u72b1\u72b2\u72b3\u72b4\u72b5\u72b6\u72b7\u72b8\u72b9\u72ba\u72bb\u72bc\u72bd\u72be\u72bf\u72c0\u72c1\u72c2\u72c3\u72c4\u72c5\u72c6\u72c7\u72c8\u72c9\u72ca\u72cb\u72cc\u72cd\u72ce\u72cf\u72d0\u72d1\u72d2\u72d3\u72d4\u72d5\u72d6\u72d7\u72d8\u72d9\u72da\u72db\u72dc\u72dd\u72de\u72df\u72e0\u72e1\u72e2\u72e3\u72e4\u72e5\u72e6\u72e7\u72e8\u72e9\u72ea\u72eb\u72ec\u72ed\u72ee\u72ef\u72f0\u72f1\u72f2\u72f3\u72f4\u72f5\u72f6\u72f7\u72f8\u72f9\u72fa\u72fb\u72fc\u72fd\u72fe\u72ff\u7300\u7301\u7302\u7303\u7304\u7305\u7306\u7307\u7308\u7309\u730a\u730b\u730c\u730d\u730e\u730f\u7310\u7311\u7312\u7313\u7314\u7315\u7316\u7317\u7318\u7319\u731a\u731b\u731c\u731d\u731e\u731f\u7320\u7321\u7322\u7323\u7324\u7325\u7326\u7327\u7328\u7329\u732a\u732b\u732c\u732d\u732e\u732f\u7330\u7331\u7332\u7333\u7334\u7335\u7336\u7337\u7338\u7339\u733a\u733b\u733c\u733d\u733e\u733f\u7340\u7341\u7342\u7343\u7344\u7345\u7346\u7347\u7348\u7349\u734a\u734b\u734c\u734d\u734e\u734f\u7350\u7351\u7352\u7353\u7354\u7355\u7356\u7357\u7358\u7359\u735a\u735b\u735c\u735d\u735e\u735f\u7360\u7361\u7362\u7363\u7364\u7365\u7366\u7367\u7368\u7369\u736a\u736b\u736c\u736d\u736e\u736f\u7370\u7371\u7372\u7373\u7374\u7375\u7376\u7377\u7378\u7379\u737a\u737b\u737c\u737d\u737e\u737f\u7380\u7381\u7382\u7383\u7384\u7385\u7386\u7387\u7388\u7389\u738a\u738b\u738c\u738d\u738e\u738f\u7390\u7391\u7392\u7393\u7394\u7395\u7396\u7397\u7398\u7399\u739a\u739b\u739c\u739d\u739e\u739f\u73a0\u73a1\u73a2\u73a3\u73a4\u73a5\u73a6\u73a7\u73a8\u73a9\u73aa\u73ab\u73ac\u73ad\u73ae\u73af\u73b0\u73b1\u73b2\u73b3\u73b4\u73b5\u73b6\u73b7\u73b8\u73b9\u73ba\u73bb\u73bc\u73bd\u73be\u73bf\u73c0\u73c1\u73c2\u73c3\u73c4\u73c5\u73c6\u73c7\u73c8\u73c9\u73ca\u73cb\u73cc\u73cd\u73ce\u73cf\u73d0\u73d1\u73d2\u73d3\u73d4\u73d5\u73d6\u73d7\u73d8\u73d9\u73da\u73db\u73dc\u73dd\u73de\u73df\u73e0\u73e1\u73e2\u73e3\u73e4\u73e5\u73e6\u73e7\u73e8\u73e9\u73ea\u73eb\u73ec\u73ed\u73ee\u73ef\u73f0\u73f1\u73f2\u73f3\u73f4\u73f5\u73f6\u73f7\u73f8\u73f9\u73fa\u73fb\u73fc\u73fd\u73fe\u73ff\u7400\u7401\u7402\u7403\u7404\u7405\u7406\u7407\u7408\u7409\u740a\u740b\u740c\u740d\u740e\u740f\u7410\u7411\u7412\u7413\u7414\u7415\u7416\u7417\u7418\u7419\u741a\u741b\u741c\u741d\u741e\u741f\u7420\u7421\u7422\u7423\u7424\u7425\u7426\u7427\u7428\u7429\u742a\u742b\u742c\u742d\u742e\u742f\u7430\u7431\u7432\u7433\u7434\u7435\u7436\u7437\u7438\u7439\u743a\u743b\u743c\u743d\u743e\u743f\u7440\u7441\u7442\u7443\u7444\u7445\u7446\u7447\u7448\u7449\u744a\u744b\u744c\u744d\u744e\u744f\u7450\u7451\u7452\u7453\u7454\u7455\u7456\u7457\u7458\u7459\u745a\u745b\u745c\u745d\u745e\u745f\u7460\u7461\u7462\u7463\u7464\u7465\u7466\u7467\u7468\u7469\u746a\u746b\u746c\u746d\u746e\u746f\u7470\u7471\u7472\u7473\u7474\u7475\u7476\u7477\u7478\u7479\u747a\u747b\u747c\u747d\u747e\u747f\u7480\u7481\u7482\u7483\u7484\u7485\u7486\u7487\u7488\u7489\u748a\u748b\u748c\u748d\u748e\u748f\u7490\u7491\u7492\u7493\u7494\u7495\u7496\u7497\u7498\u7499\u749a\u749b\u749c\u749d\u749e\u749f\u74a0\u74a1\u74a2\u74a3\u74a4\u74a5\u74a6\u74a7\u74a8\u74a9\u74aa\u74ab\u74ac\u74ad\u74ae\u74af\u74b0\u74b1\u74b2\u74b3\u74b4\u74b5\u74b6\u74b7\u74b8\u74b9\u74ba\u74bb\u74bc\u74bd\u74be\u74bf\u74c0\u74c1\u74c2\u74c3\u74c4\u74c5\u74c6\u74c7\u74c8\u74c9\u74ca\u74cb\u74cc\u74cd\u74ce\u74cf\u74d0\u74d1\u74d2\u74d3\u74d4\u74d5\u74d6\u74d7\u74d8\u74d9\u74da\u74db\u74dc\u74dd\u74de\u74df\u74e0\u74e1\u74e2\u74e3\u74e4\u74e5\u74e6\u74e7\u74e8\u74e9\u74ea\u74eb\u74ec\u74ed\u74ee\u74ef\u74f0\u74f1\u74f2\u74f3\u74f4\u74f5\u74f6\u74f7\u74f8\u74f9\u74fa\u74fb\u74fc\u74fd\u74fe\u74ff\u7500\u7501\u7502\u7503\u7504\u7505\u7506\u7507\u7508\u7509\u750a\u750b\u750c\u750d\u750e\u750f\u7510\u7511\u7512\u7513\u7514\u7515\u7516\u7517\u7518\u7519\u751a\u751b\u751c\u751d\u751e\u751f\u7520\u7521\u7522\u7523\u7524\u7525\u7526\u7527\u7528\u7529\u752a\u752b\u752c\u752d\u752e\u752f\u7530\u7531\u7532\u7533\u7534\u7535\u7536\u7537\u7538\u7539\u753a\u753b\u753c\u753d\u753e\u753f\u7540\u7541\u7542\u7543\u7544\u7545\u7546\u7547\u7548\u7549\u754a\u754b\u754c\u754d\u754e\u754f\u7550\u7551\u7552\u7553\u7554\u7555\u7556\u7557\u7558\u7559\u755a\u755b\u755c\u755d\u755e\u755f\u7560\u7561\u7562\u7563\u7564\u7565\u7566\u7567\u7568\u7569\u756a\u756b\u756c\u756d\u756e\u756f\u7570\u7571\u7572\u7573\u7574\u7575\u7576\u7577\u7578\u7579\u757a\u757b\u757c\u757d\u757e\u757f\u7580\u7581\u7582\u7583\u7584\u7585\u7586\u7587\u7588\u7589\u758a\u758b\u758c\u758d\u758e\u758f\u7590\u7591\u7592\u7593\u7594\u7595\u7596\u7597\u7598\u7599\u759a\u759b\u759c\u759d\u759e\u759f\u75a0\u75a1\u75a2\u75a3\u75a4\u75a5\u75a6\u75a7\u75a8\u75a9\u75aa\u75ab\u75ac\u75ad\u75ae\u75af\u75b0\u75b1\u75b2\u75b3\u75b4\u75b5\u75b6\u75b7\u75b8\u75b9\u75ba\u75bb\u75bc\u75bd\u75be\u75bf\u75c0\u75c1\u75c2\u75c3\u75c4\u75c5\u75c6\u75c7\u75c8\u75c9\u75ca\u75cb\u75cc\u75cd\u75ce\u75cf\u75d0\u75d1\u75d2\u75d3\u75d4\u75d5\u75d6\u75d7\u75d8\u75d9\u75da\u75db\u75dc\u75dd\u75de\u75df\u75e0\u75e1\u75e2\u75e3\u75e4\u75e5\u75e6\u75e7\u75e8\u75e9\u75ea\u75eb\u75ec\u75ed\u75ee\u75ef\u75f0\u75f1\u75f2\u75f3\u75f4\u75f5\u75f6\u75f7\u75f8\u75f9\u75fa\u75fb\u75fc\u75fd\u75fe\u75ff\u7600\u7601\u7602\u7603\u7604\u7605\u7606\u7607\u7608\u7609\u760a\u760b\u760c\u760d\u760e\u760f\u7610\u7611\u7612\u7613\u7614\u7615\u7616\u7617\u7618\u7619\u761a\u761b\u761c\u761d\u761e\u761f\u7620\u7621\u7622\u7623\u7624\u7625\u7626\u7627\u7628\u7629\u762a\u762b\u762c\u762d\u762e\u762f\u7630\u7631\u7632\u7633\u7634\u7635\u7636\u7637\u7638\u7639\u763a\u763b\u763c\u763d\u763e\u763f\u7640\u7641\u7642\u7643\u7644\u7645\u7646\u7647\u7648\u7649\u764a\u764b\u764c\u764d\u764e\u764f\u7650\u7651\u7652\u7653\u7654\u7655\u7656\u7657\u7658\u7659\u765a\u765b\u765c\u765d\u765e\u765f\u7660\u7661\u7662\u7663\u7664\u7665\u7666\u7667\u7668\u7669\u766a\u766b\u766c\u766d\u766e\u766f\u7670\u7671\u7672\u7673\u7674\u7675\u7676\u7677\u7678\u7679\u767a\u767b\u767c\u767d\u767e\u767f\u7680\u7681\u7682\u7683\u7684\u7685\u7686\u7687\u7688\u7689\u768a\u768b\u768c\u768d\u768e\u768f\u7690\u7691\u7692\u7693\u7694\u7695\u7696\u7697\u7698\u7699\u769a\u769b\u769c\u769d\u769e\u769f\u76a0\u76a1\u76a2\u76a3\u76a4\u76a5\u76a6\u76a7\u76a8\u76a9\u76aa\u76ab\u76ac\u76ad\u76ae\u76af\u76b0\u76b1\u76b2\u76b3\u76b4\u76b5\u76b6\u76b7\u76b8\u76b9\u76ba\u76bb\u76bc\u76bd\u76be\u76bf\u76c0\u76c1\u76c2\u76c3\u76c4\u76c5\u76c6\u76c7\u76c8\u76c9\u76ca\u76cb\u76cc\u76cd\u76ce\u76cf\u76d0\u76d1\u76d2\u76d3\u76d4\u76d5\u76d6\u76d7\u76d8\u76d9\u76da\u76db\u76dc\u76dd\u76de\u76df\u76e0\u76e1\u76e2\u76e3\u76e4\u76e5\u76e6\u76e7\u76e8\u76e9\u76ea\u76eb\u76ec\u76ed\u76ee\u76ef\u76f0\u76f1\u76f2\u76f3\u76f4\u76f5\u76f6\u76f7\u76f8\u76f9\u76fa\u76fb\u76fc\u76fd\u76fe\u76ff\u7700\u7701\u7702\u7703\u7704\u7705\u7706\u7707\u7708\u7709\u770a\u770b\u770c\u770d\u770e\u770f\u7710\u7711\u7712\u7713\u7714\u7715\u7716\u7717\u7718\u7719\u771a\u771b\u771c\u771d\u771e\u771f\u7720\u7721\u7722\u7723\u7724\u7725\u7726\u7727\u7728\u7729\u772a\u772b\u772c\u772d\u772e\u772f\u7730\u7731\u7732\u7733\u7734\u7735\u7736\u7737\u7738\u7739\u773a\u773b\u773c\u773d\u773e\u773f\u7740\u7741\u7742\u7743\u7744\u7745\u7746\u7747\u7748\u7749\u774a\u774b\u774c\u774d\u774e\u774f\u7750\u7751\u7752\u7753\u7754\u7755\u7756\u7757\u7758\u7759\u775a\u775b\u775c\u775d\u775e\u775f\u7760\u7761\u7762\u7763\u7764\u7765\u7766\u7767\u7768\u7769\u776a\u776b\u776c\u776d\u776e\u776f\u7770\u7771\u7772\u7773\u7774\u7775\u7776\u7777\u7778\u7779\u777a\u777b\u777c\u777d\u777e\u777f\u7780\u7781\u7782\u7783\u7784\u7785\u7786\u7787\u7788\u7789\u778a\u778b\u778c\u778d\u778e\u778f\u7790\u7791\u7792\u7793\u7794\u7795\u7796\u7797\u7798\u7799\u779a\u779b\u779c\u779d\u779e\u779f\u77a0\u77a1\u77a2\u77a3\u77a4\u77a5\u77a6\u77a7\u77a8\u77a9\u77aa\u77ab\u77ac\u77ad\u77ae\u77af\u77b0\u77b1\u77b2\u77b3\u77b4\u77b5\u77b6\u77b7\u77b8\u77b9\u77ba\u77bb\u77bc\u77bd\u77be\u77bf\u77c0\u77c1\u77c2\u77c3\u77c4\u77c5\u77c6\u77c7\u77c8\u77c9\u77ca\u77cb\u77cc\u77cd\u77ce\u77cf\u77d0\u77d1\u77d2\u77d3\u77d4\u77d5\u77d6\u77d7\u77d8\u77d9\u77da\u77db\u77dc\u77dd\u77de\u77df\u77e0\u77e1\u77e2\u77e3\u77e4\u77e5\u77e6\u77e7\u77e8\u77e9\u77ea\u77eb\u77ec\u77ed\u77ee\u77ef\u77f0\u77f1\u77f2\u77f3\u77f4\u77f5\u77f6\u77f7\u77f8\u77f9\u77fa\u77fb\u77fc\u77fd\u77fe\u77ff\u7800\u7801\u7802\u7803\u7804\u7805\u7806\u7807\u7808\u7809\u780a\u780b\u780c\u780d\u780e\u780f\u7810\u7811\u7812\u7813\u7814\u7815\u7816\u7817\u7818\u7819\u781a\u781b\u781c\u781d\u781e\u781f\u7820\u7821\u7822\u7823\u7824\u7825\u7826\u7827\u7828\u7829\u782a\u782b\u782c\u782d\u782e\u782f\u7830\u7831\u7832\u7833\u7834\u7835\u7836\u7837\u7838\u7839\u783a\u783b\u783c\u783d\u783e\u783f\u7840\u7841\u7842\u7843\u7844\u7845\u7846\u7847\u7848\u7849\u784a\u784b\u784c\u784d\u784e\u784f\u7850\u7851\u7852\u7853\u7854\u7855\u7856\u7857\u7858\u7859\u785a\u785b\u785c\u785d\u785e\u785f\u7860\u7861\u7862\u7863\u7864\u7865\u7866\u7867\u7868\u7869\u786a\u786b\u786c\u786d\u786e\u786f\u7870\u7871\u7872\u7873\u7874\u7875\u7876\u7877\u7878\u7879\u787a\u787b\u787c\u787d\u787e\u787f\u7880\u7881\u7882\u7883\u7884\u7885\u7886\u7887\u7888\u7889\u788a\u788b\u788c\u788d\u788e\u788f\u7890\u7891\u7892\u7893\u7894\u7895\u7896\u7897\u7898\u7899\u789a\u789b\u789c\u789d\u789e\u789f\u78a0\u78a1\u78a2\u78a3\u78a4\u78a5\u78a6\u78a7\u78a8\u78a9\u78aa\u78ab\u78ac\u78ad\u78ae\u78af\u78b0\u78b1\u78b2\u78b3\u78b4\u78b5\u78b6\u78b7\u78b8\u78b9\u78ba\u78bb\u78bc\u78bd\u78be\u78bf\u78c0\u78c1\u78c2\u78c3\u78c4\u78c5\u78c6\u78c7\u78c8\u78c9\u78ca\u78cb\u78cc\u78cd\u78ce\u78cf\u78d0\u78d1\u78d2\u78d3\u78d4\u78d5\u78d6\u78d7\u78d8\u78d9\u78da\u78db\u78dc\u78dd\u78de\u78df\u78e0\u78e1\u78e2\u78e3\u78e4\u78e5\u78e6\u78e7\u78e8\u78e9\u78ea\u78eb\u78ec\u78ed\u78ee\u78ef\u78f0\u78f1\u78f2\u78f3\u78f4\u78f5\u78f6\u78f7\u78f8\u78f9\u78fa\u78fb\u78fc\u78fd\u78fe\u78ff\u7900\u7901\u7902\u7903\u7904\u7905\u7906\u7907\u7908\u7909\u790a\u790b\u790c\u790d\u790e\u790f\u7910\u7911\u7912\u7913\u7914\u7915\u7916\u7917\u7918\u7919\u791a\u791b\u791c\u791d\u791e\u791f\u7920\u7921\u7922\u7923\u7924\u7925\u7926\u7927\u7928\u7929\u792a\u792b\u792c\u792d\u792e\u792f\u7930\u7931\u7932\u7933\u7934\u7935\u7936\u7937\u7938\u7939\u793a\u793b\u793c\u793d\u793e\u793f\u7940\u7941\u7942\u7943\u7944\u7945\u7946\u7947\u7948\u7949\u794a\u794b\u794c\u794d\u794e\u794f\u7950\u7951\u7952\u7953\u7954\u7955\u7956\u7957\u7958\u7959\u795a\u795b\u795c\u795d\u795e\u795f\u7960\u7961\u7962\u7963\u7964\u7965\u7966\u7967\u7968\u7969\u796a\u796b\u796c\u796d\u796e\u796f\u7970\u7971\u7972\u7973\u7974\u7975\u7976\u7977\u7978\u7979\u797a\u797b\u797c\u797d\u797e\u797f\u7980\u7981\u7982\u7983\u7984\u7985\u7986\u7987\u7988\u7989\u798a\u798b\u798c\u798d\u798e\u798f\u7990\u7991\u7992\u7993\u7994\u7995\u7996\u7997\u7998\u7999\u799a\u799b\u799c\u799d\u799e\u799f\u79a0\u79a1\u79a2\u79a3\u79a4\u79a5\u79a6\u79a7\u79a8\u79a9\u79aa\u79ab\u79ac\u79ad\u79ae\u79af\u79b0\u79b1\u79b2\u79b3\u79b4\u79b5\u79b6\u79b7\u79b8\u79b9\u79ba\u79bb\u79bc\u79bd\u79be\u79bf\u79c0\u79c1\u79c2\u79c3\u79c4\u79c5\u79c6\u79c7\u79c8\u79c9\u79ca\u79cb\u79cc\u79cd\u79ce\u79cf\u79d0\u79d1\u79d2\u79d3\u79d4\u79d5\u79d6\u79d7\u79d8\u79d9\u79da\u79db\u79dc\u79dd\u79de\u79df\u79e0\u79e1\u79e2\u79e3\u79e4\u79e5\u79e6\u79e7\u79e8\u79e9\u79ea\u79eb\u79ec\u79ed\u79ee\u79ef\u79f0\u79f1\u79f2\u79f3\u79f4\u79f5\u79f6\u79f7\u79f8\u79f9\u79fa\u79fb\u79fc\u79fd\u79fe\u79ff\u7a00\u7a01\u7a02\u7a03\u7a04\u7a05\u7a06\u7a07\u7a08\u7a09\u7a0a\u7a0b\u7a0c\u7a0d\u7a0e\u7a0f\u7a10\u7a11\u7a12\u7a13\u7a14\u7a15\u7a16\u7a17\u7a18\u7a19\u7a1a\u7a1b\u7a1c\u7a1d\u7a1e\u7a1f\u7a20\u7a21\u7a22\u7a23\u7a24\u7a25\u7a26\u7a27\u7a28\u7a29\u7a2a\u7a2b\u7a2c\u7a2d\u7a2e\u7a2f\u7a30\u7a31\u7a32\u7a33\u7a34\u7a35\u7a36\u7a37\u7a38\u7a39\u7a3a\u7a3b\u7a3c\u7a3d\u7a3e\u7a3f\u7a40\u7a41\u7a42\u7a43\u7a44\u7a45\u7a46\u7a47\u7a48\u7a49\u7a4a\u7a4b\u7a4c\u7a4d\u7a4e\u7a4f\u7a50\u7a51\u7a52\u7a53\u7a54\u7a55\u7a56\u7a57\u7a58\u7a59\u7a5a\u7a5b\u7a5c\u7a5d\u7a5e\u7a5f\u7a60\u7a61\u7a62\u7a63\u7a64\u7a65\u7a66\u7a67\u7a68\u7a69\u7a6a\u7a6b\u7a6c\u7a6d\u7a6e\u7a6f\u7a70\u7a71\u7a72\u7a73\u7a74\u7a75\u7a76\u7a77\u7a78\u7a79\u7a7a\u7a7b\u7a7c\u7a7d\u7a7e\u7a7f\u7a80\u7a81\u7a82\u7a83\u7a84\u7a85\u7a86\u7a87\u7a88\u7a89\u7a8a\u7a8b\u7a8c\u7a8d\u7a8e\u7a8f\u7a90\u7a91\u7a92\u7a93\u7a94\u7a95\u7a96\u7a97\u7a98\u7a99\u7a9a\u7a9b\u7a9c\u7a9d\u7a9e\u7a9f\u7aa0\u7aa1\u7aa2\u7aa3\u7aa4\u7aa5\u7aa6\u7aa7\u7aa8\u7aa9\u7aaa\u7aab\u7aac\u7aad\u7aae\u7aaf\u7ab0\u7ab1\u7ab2\u7ab3\u7ab4\u7ab5\u7ab6\u7ab7\u7ab8\u7ab9\u7aba\u7abb\u7abc\u7abd\u7abe\u7abf\u7ac0\u7ac1\u7ac2\u7ac3\u7ac4\u7ac5\u7ac6\u7ac7\u7ac8\u7ac9\u7aca\u7acb\u7acc\u7acd\u7ace\u7acf\u7ad0\u7ad1\u7ad2\u7ad3\u7ad4\u7ad5\u7ad6\u7ad7\u7ad8\u7ad9\u7ada\u7adb\u7adc\u7add\u7ade\u7adf\u7ae0\u7ae1\u7ae2\u7ae3\u7ae4\u7ae5\u7ae6\u7ae7\u7ae8\u7ae9\u7aea\u7aeb\u7aec\u7aed\u7aee\u7aef\u7af0\u7af1\u7af2\u7af3\u7af4\u7af5\u7af6\u7af7\u7af8\u7af9\u7afa\u7afb\u7afc\u7afd\u7afe\u7aff\u7b00\u7b01\u7b02\u7b03\u7b04\u7b05\u7b06\u7b07\u7b08\u7b09\u7b0a\u7b0b\u7b0c\u7b0d\u7b0e\u7b0f\u7b10\u7b11\u7b12\u7b13\u7b14\u7b15\u7b16\u7b17\u7b18\u7b19\u7b1a\u7b1b\u7b1c\u7b1d\u7b1e\u7b1f\u7b20\u7b21\u7b22\u7b23\u7b24\u7b25\u7b26\u7b27\u7b28\u7b29\u7b2a\u7b2b\u7b2c\u7b2d\u7b2e\u7b2f\u7b30\u7b31\u7b32\u7b33\u7b34\u7b35\u7b36\u7b37\u7b38\u7b39\u7b3a\u7b3b\u7b3c\u7b3d\u7b3e\u7b3f\u7b40\u7b41\u7b42\u7b43\u7b44\u7b45\u7b46\u7b47\u7b48\u7b49\u7b4a\u7b4b\u7b4c\u7b4d\u7b4e\u7b4f\u7b50\u7b51\u7b52\u7b53\u7b54\u7b55\u7b56\u7b57\u7b58\u7b59\u7b5a\u7b5b\u7b5c\u7b5d\u7b5e\u7b5f\u7b60\u7b61\u7b62\u7b63\u7b64\u7b65\u7b66\u7b67\u7b68\u7b69\u7b6a\u7b6b\u7b6c\u7b6d\u7b6e\u7b6f\u7b70\u7b71\u7b72\u7b73\u7b74\u7b75\u7b76\u7b77\u7b78\u7b79\u7b7a\u7b7b\u7b7c\u7b7d\u7b7e\u7b7f\u7b80\u7b81\u7b82\u7b83\u7b84\u7b85\u7b86\u7b87\u7b88\u7b89\u7b8a\u7b8b\u7b8c\u7b8d\u7b8e\u7b8f\u7b90\u7b91\u7b92\u7b93\u7b94\u7b95\u7b96\u7b97\u7b98\u7b99\u7b9a\u7b9b\u7b9c\u7b9d\u7b9e\u7b9f\u7ba0\u7ba1\u7ba2\u7ba3\u7ba4\u7ba5\u7ba6\u7ba7\u7ba8\u7ba9\u7baa\u7bab\u7bac\u7bad\u7bae\u7baf\u7bb0\u7bb1\u7bb2\u7bb3\u7bb4\u7bb5\u7bb6\u7bb7\u7bb8\u7bb9\u7bba\u7bbb\u7bbc\u7bbd\u7bbe\u7bbf\u7bc0\u7bc1\u7bc2\u7bc3\u7bc4\u7bc5\u7bc6\u7bc7\u7bc8\u7bc9\u7bca\u7bcb\u7bcc\u7bcd\u7bce\u7bcf\u7bd0\u7bd1\u7bd2\u7bd3\u7bd4\u7bd5\u7bd6\u7bd7\u7bd8\u7bd9\u7bda\u7bdb\u7bdc\u7bdd\u7bde\u7bdf\u7be0\u7be1\u7be2\u7be3\u7be4\u7be5\u7be6\u7be7\u7be8\u7be9\u7bea\u7beb\u7bec\u7bed\u7bee\u7bef\u7bf0\u7bf1\u7bf2\u7bf3\u7bf4\u7bf5\u7bf6\u7bf7\u7bf8\u7bf9\u7bfa\u7bfb\u7bfc\u7bfd\u7bfe\u7bff\u7c00\u7c01\u7c02\u7c03\u7c04\u7c05\u7c06\u7c07\u7c08\u7c09\u7c0a\u7c0b\u7c0c\u7c0d\u7c0e\u7c0f\u7c10\u7c11\u7c12\u7c13\u7c14\u7c15\u7c16\u7c17\u7c18\u7c19\u7c1a\u7c1b\u7c1c\u7c1d\u7c1e\u7c1f\u7c20\u7c21\u7c22\u7c23\u7c24\u7c25\u7c26\u7c27\u7c28\u7c29\u7c2a\u7c2b\u7c2c\u7c2d\u7c2e\u7c2f\u7c30\u7c31\u7c32\u7c33\u7c34\u7c35\u7c36\u7c37\u7c38\u7c39\u7c3a\u7c3b\u7c3c\u7c3d\u7c3e\u7c3f\u7c40\u7c41\u7c42\u7c43\u7c44\u7c45\u7c46\u7c47\u7c48\u7c49\u7c4a\u7c4b\u7c4c\u7c4d\u7c4e\u7c4f\u7c50\u7c51\u7c52\u7c53\u7c54\u7c55\u7c56\u7c57\u7c58\u7c59\u7c5a\u7c5b\u7c5c\u7c5d\u7c5e\u7c5f\u7c60\u7c61\u7c62\u7c63\u7c64\u7c65\u7c66\u7c67\u7c68\u7c69\u7c6a\u7c6b\u7c6c\u7c6d\u7c6e\u7c6f\u7c70\u7c71\u7c72\u7c73\u7c74\u7c75\u7c76\u7c77\u7c78\u7c79\u7c7a\u7c7b\u7c7c\u7c7d\u7c7e\u7c7f\u7c80\u7c81\u7c82\u7c83\u7c84\u7c85\u7c86\u7c87\u7c88\u7c89\u7c8a\u7c8b\u7c8c\u7c8d\u7c8e\u7c8f\u7c90\u7c91\u7c92\u7c93\u7c94\u7c95\u7c96\u7c97\u7c98\u7c99\u7c9a\u7c9b\u7c9c\u7c9d\u7c9e\u7c9f\u7ca0\u7ca1\u7ca2\u7ca3\u7ca4\u7ca5\u7ca6\u7ca7\u7ca8\u7ca9\u7caa\u7cab\u7cac\u7cad\u7cae\u7caf\u7cb0\u7cb1\u7cb2\u7cb3\u7cb4\u7cb5\u7cb6\u7cb7\u7cb8\u7cb9\u7cba\u7cbb\u7cbc\u7cbd\u7cbe\u7cbf\u7cc0\u7cc1\u7cc2\u7cc3\u7cc4\u7cc5\u7cc6\u7cc7\u7cc8\u7cc9\u7cca\u7ccb\u7ccc\u7ccd\u7cce\u7ccf\u7cd0\u7cd1\u7cd2\u7cd3\u7cd4\u7cd5\u7cd6\u7cd7\u7cd8\u7cd9\u7cda\u7cdb\u7cdc\u7cdd\u7cde\u7cdf\u7ce0\u7ce1\u7ce2\u7ce3\u7ce4\u7ce5\u7ce6\u7ce7\u7ce8\u7ce9\u7cea\u7ceb\u7cec\u7ced\u7cee\u7cef\u7cf0\u7cf1\u7cf2\u7cf3\u7cf4\u7cf5\u7cf6\u7cf7\u7cf8\u7cf9\u7cfa\u7cfb\u7cfc\u7cfd\u7cfe\u7cff\u7d00\u7d01\u7d02\u7d03\u7d04\u7d05\u7d06\u7d07\u7d08\u7d09\u7d0a\u7d0b\u7d0c\u7d0d\u7d0e\u7d0f\u7d10\u7d11\u7d12\u7d13\u7d14\u7d15\u7d16\u7d17\u7d18\u7d19\u7d1a\u7d1b\u7d1c\u7d1d\u7d1e\u7d1f\u7d20\u7d21\u7d22\u7d23\u7d24\u7d25\u7d26\u7d27\u7d28\u7d29\u7d2a\u7d2b\u7d2c\u7d2d\u7d2e\u7d2f\u7d30\u7d31\u7d32\u7d33\u7d34\u7d35\u7d36\u7d37\u7d38\u7d39\u7d3a\u7d3b\u7d3c\u7d3d\u7d3e\u7d3f\u7d40\u7d41\u7d42\u7d43\u7d44\u7d45\u7d46\u7d47\u7d48\u7d49\u7d4a\u7d4b\u7d4c\u7d4d\u7d4e\u7d4f\u7d50\u7d51\u7d52\u7d53\u7d54\u7d55\u7d56\u7d57\u7d58\u7d59\u7d5a\u7d5b\u7d5c\u7d5d\u7d5e\u7d5f\u7d60\u7d61\u7d62\u7d63\u7d64\u7d65\u7d66\u7d67\u7d68\u7d69\u7d6a\u7d6b\u7d6c\u7d6d\u7d6e\u7d6f\u7d70\u7d71\u7d72\u7d73\u7d74\u7d75\u7d76\u7d77\u7d78\u7d79\u7d7a\u7d7b\u7d7c\u7d7d\u7d7e\u7d7f\u7d80\u7d81\u7d82\u7d83\u7d84\u7d85\u7d86\u7d87\u7d88\u7d89\u7d8a\u7d8b\u7d8c\u7d8d\u7d8e\u7d8f\u7d90\u7d91\u7d92\u7d93\u7d94\u7d95\u7d96\u7d97\u7d98\u7d99\u7d9a\u7d9b\u7d9c\u7d9d\u7d9e\u7d9f\u7da0\u7da1\u7da2\u7da3\u7da4\u7da5\u7da6\u7da7\u7da8\u7da9\u7daa\u7dab\u7dac\u7dad\u7dae\u7daf\u7db0\u7db1\u7db2\u7db3\u7db4\u7db5\u7db6\u7db7\u7db8\u7db9\u7dba\u7dbb\u7dbc\u7dbd\u7dbe\u7dbf\u7dc0\u7dc1\u7dc2\u7dc3\u7dc4\u7dc5\u7dc6\u7dc7\u7dc8\u7dc9\u7dca\u7dcb\u7dcc\u7dcd\u7dce\u7dcf\u7dd0\u7dd1\u7dd2\u7dd3\u7dd4\u7dd5\u7dd6\u7dd7\u7dd8\u7dd9\u7dda\u7ddb\u7ddc\u7ddd\u7dde\u7ddf\u7de0\u7de1\u7de2\u7de3\u7de4\u7de5\u7de6\u7de7\u7de8\u7de9\u7dea\u7deb\u7dec\u7ded\u7dee\u7def\u7df0\u7df1\u7df2\u7df3\u7df4\u7df5\u7df6\u7df7\u7df8\u7df9\u7dfa\u7dfb\u7dfc\u7dfd\u7dfe\u7dff\u7e00\u7e01\u7e02\u7e03\u7e04\u7e05\u7e06\u7e07\u7e08\u7e09\u7e0a\u7e0b\u7e0c\u7e0d\u7e0e\u7e0f\u7e10\u7e11\u7e12\u7e13\u7e14\u7e15\u7e16\u7e17\u7e18\u7e19\u7e1a\u7e1b\u7e1c\u7e1d\u7e1e\u7e1f\u7e20\u7e21\u7e22\u7e23\u7e24\u7e25\u7e26\u7e27\u7e28\u7e29\u7e2a\u7e2b\u7e2c\u7e2d\u7e2e\u7e2f\u7e30\u7e31\u7e32\u7e33\u7e34\u7e35\u7e36\u7e37\u7e38\u7e39\u7e3a\u7e3b\u7e3c\u7e3d\u7e3e\u7e3f\u7e40\u7e41\u7e42\u7e43\u7e44\u7e45\u7e46\u7e47\u7e48\u7e49\u7e4a\u7e4b\u7e4c\u7e4d\u7e4e\u7e4f\u7e50\u7e51\u7e52\u7e53\u7e54\u7e55\u7e56\u7e57\u7e58\u7e59\u7e5a\u7e5b\u7e5c\u7e5d\u7e5e\u7e5f\u7e60\u7e61\u7e62\u7e63\u7e64\u7e65\u7e66\u7e67\u7e68\u7e69\u7e6a\u7e6b\u7e6c\u7e6d\u7e6e\u7e6f\u7e70\u7e71\u7e72\u7e73\u7e74\u7e75\u7e76\u7e77\u7e78\u7e79\u7e7a\u7e7b\u7e7c\u7e7d\u7e7e\u7e7f\u7e80\u7e81\u7e82\u7e83\u7e84\u7e85\u7e86\u7e87\u7e88\u7e89\u7e8a\u7e8b\u7e8c\u7e8d\u7e8e\u7e8f\u7e90\u7e91\u7e92\u7e93\u7e94\u7e95\u7e96\u7e97\u7e98\u7e99\u7e9a\u7e9b\u7e9c\u7e9d\u7e9e\u7e9f\u7ea0\u7ea1\u7ea2\u7ea3\u7ea4\u7ea5\u7ea6\u7ea7\u7ea8\u7ea9\u7eaa\u7eab\u7eac\u7ead\u7eae\u7eaf\u7eb0\u7eb1\u7eb2\u7eb3\u7eb4\u7eb5\u7eb6\u7eb7\u7eb8\u7eb9\u7eba\u7ebb\u7ebc\u7ebd\u7ebe\u7ebf\u7ec0\u7ec1\u7ec2\u7ec3\u7ec4\u7ec5\u7ec6\u7ec7\u7ec8\u7ec9\u7eca\u7ecb\u7ecc\u7ecd\u7ece\u7ecf\u7ed0\u7ed1\u7ed2\u7ed3\u7ed4\u7ed5\u7ed6\u7ed7\u7ed8\u7ed9\u7eda\u7edb\u7edc\u7edd\u7ede\u7edf\u7ee0\u7ee1\u7ee2\u7ee3\u7ee4\u7ee5\u7ee6\u7ee7\u7ee8\u7ee9\u7eea\u7eeb\u7eec\u7eed\u7eee\u7eef\u7ef0\u7ef1\u7ef2\u7ef3\u7ef4\u7ef5\u7ef6\u7ef7\u7ef8\u7ef9\u7efa\u7efb\u7efc\u7efd\u7efe\u7eff\u7f00\u7f01\u7f02\u7f03\u7f04\u7f05\u7f06\u7f07\u7f08\u7f09\u7f0a\u7f0b\u7f0c\u7f0d\u7f0e\u7f0f\u7f10\u7f11\u7f12\u7f13\u7f14\u7f15\u7f16\u7f17\u7f18\u7f19\u7f1a\u7f1b\u7f1c\u7f1d\u7f1e\u7f1f\u7f20\u7f21\u7f22\u7f23\u7f24\u7f25\u7f26\u7f27\u7f28\u7f29\u7f2a\u7f2b\u7f2c\u7f2d\u7f2e\u7f2f\u7f30\u7f31\u7f32\u7f33\u7f34\u7f35\u7f36\u7f37\u7f38\u7f39\u7f3a\u7f3b\u7f3c\u7f3d\u7f3e\u7f3f\u7f40\u7f41\u7f42\u7f43\u7f44\u7f45\u7f46\u7f47\u7f48\u7f49\u7f4a\u7f4b\u7f4c\u7f4d\u7f4e\u7f4f\u7f50\u7f51\u7f52\u7f53\u7f54\u7f55\u7f56\u7f57\u7f58\u7f59\u7f5a\u7f5b\u7f5c\u7f5d\u7f5e\u7f5f\u7f60\u7f61\u7f62\u7f63\u7f64\u7f65\u7f66\u7f67\u7f68\u7f69\u7f6a\u7f6b\u7f6c\u7f6d\u7f6e\u7f6f\u7f70\u7f71\u7f72\u7f73\u7f74\u7f75\u7f76\u7f77\u7f78\u7f79\u7f7a\u7f7b\u7f7c\u7f7d\u7f7e\u7f7f\u7f80\u7f81\u7f82\u7f83\u7f84\u7f85\u7f86\u7f87\u7f88\u7f89\u7f8a\u7f8b\u7f8c\u7f8d\u7f8e\u7f8f\u7f90\u7f91\u7f92\u7f93\u7f94\u7f95\u7f96\u7f97\u7f98\u7f99\u7f9a\u7f9b\u7f9c\u7f9d\u7f9e\u7f9f\u7fa0\u7fa1\u7fa2\u7fa3\u7fa4\u7fa5\u7fa6\u7fa7\u7fa8\u7fa9\u7faa\u7fab\u7fac\u7fad\u7fae\u7faf\u7fb0\u7fb1\u7fb2\u7fb3\u7fb4\u7fb5\u7fb6\u7fb7\u7fb8\u7fb9\u7fba\u7fbb\u7fbc\u7fbd\u7fbe\u7fbf\u7fc0\u7fc1\u7fc2\u7fc3\u7fc4\u7fc5\u7fc6\u7fc7\u7fc8\u7fc9\u7fca\u7fcb\u7fcc\u7fcd\u7fce\u7fcf\u7fd0\u7fd1\u7fd2\u7fd3\u7fd4\u7fd5\u7fd6\u7fd7\u7fd8\u7fd9\u7fda\u7fdb\u7fdc\u7fdd\u7fde\u7fdf\u7fe0\u7fe1\u7fe2\u7fe3\u7fe4\u7fe5\u7fe6\u7fe7\u7fe8\u7fe9\u7fea\u7feb\u7fec\u7fed\u7fee\u7fef\u7ff0\u7ff1\u7ff2\u7ff3\u7ff4\u7ff5\u7ff6\u7ff7\u7ff8\u7ff9\u7ffa\u7ffb\u7ffc\u7ffd\u7ffe\u7fff\u8000\u8001\u8002\u8003\u8004\u8005\u8006\u8007\u8008\u8009\u800a\u800b\u800c\u800d\u800e\u800f\u8010\u8011\u8012\u8013\u8014\u8015\u8016\u8017\u8018\u8019\u801a\u801b\u801c\u801d\u801e\u801f\u8020\u8021\u8022\u8023\u8024\u8025\u8026\u8027\u8028\u8029\u802a\u802b\u802c\u802d\u802e\u802f\u8030\u8031\u8032\u8033\u8034\u8035\u8036\u8037\u8038\u8039\u803a\u803b\u803c\u803d\u803e\u803f\u8040\u8041\u8042\u8043\u8044\u8045\u8046\u8047\u8048\u8049\u804a\u804b\u804c\u804d\u804e\u804f\u8050\u8051\u8052\u8053\u8054\u8055\u8056\u8057\u8058\u8059\u805a\u805b\u805c\u805d\u805e\u805f\u8060\u8061\u8062\u8063\u8064\u8065\u8066\u8067\u8068\u8069\u806a\u806b\u806c\u806d\u806e\u806f\u8070\u8071\u8072\u8073\u8074\u8075\u8076\u8077\u8078\u8079\u807a\u807b\u807c\u807d\u807e\u807f\u8080\u8081\u8082\u8083\u8084\u8085\u8086\u8087\u8088\u8089\u808a\u808b\u808c\u808d\u808e\u808f\u8090\u8091\u8092\u8093\u8094\u8095\u8096\u8097\u8098\u8099\u809a\u809b\u809c\u809d\u809e\u809f\u80a0\u80a1\u80a2\u80a3\u80a4\u80a5\u80a6\u80a7\u80a8\u80a9\u80aa\u80ab\u80ac\u80ad\u80ae\u80af\u80b0\u80b1\u80b2\u80b3\u80b4\u80b5\u80b6\u80b7\u80b8\u80b9\u80ba\u80bb\u80bc\u80bd\u80be\u80bf\u80c0\u80c1\u80c2\u80c3\u80c4\u80c5\u80c6\u80c7\u80c8\u80c9\u80ca\u80cb\u80cc\u80cd\u80ce\u80cf\u80d0\u80d1\u80d2\u80d3\u80d4\u80d5\u80d6\u80d7\u80d8\u80d9\u80da\u80db\u80dc\u80dd\u80de\u80df\u80e0\u80e1\u80e2\u80e3\u80e4\u80e5\u80e6\u80e7\u80e8\u80e9\u80ea\u80eb\u80ec\u80ed\u80ee\u80ef\u80f0\u80f1\u80f2\u80f3\u80f4\u80f5\u80f6\u80f7\u80f8\u80f9\u80fa\u80fb\u80fc\u80fd\u80fe\u80ff\u8100\u8101\u8102\u8103\u8104\u8105\u8106\u8107\u8108\u8109\u810a\u810b\u810c\u810d\u810e\u810f\u8110\u8111\u8112\u8113\u8114\u8115\u8116\u8117\u8118\u8119\u811a\u811b\u811c\u811d\u811e\u811f\u8120\u8121\u8122\u8123\u8124\u8125\u8126\u8127\u8128\u8129\u812a\u812b\u812c\u812d\u812e\u812f\u8130\u8131\u8132\u8133\u8134\u8135\u8136\u8137\u8138\u8139\u813a\u813b\u813c\u813d\u813e\u813f\u8140\u8141\u8142\u8143\u8144\u8145\u8146\u8147\u8148\u8149\u814a\u814b\u814c\u814d\u814e\u814f\u8150\u8151\u8152\u8153\u8154\u8155\u8156\u8157\u8158\u8159\u815a\u815b\u815c\u815d\u815e\u815f\u8160\u8161\u8162\u8163\u8164\u8165\u8166\u8167\u8168\u8169\u816a\u816b\u816c\u816d\u816e\u816f\u8170\u8171\u8172\u8173\u8174\u8175\u8176\u8177\u8178\u8179\u817a\u817b\u817c\u817d\u817e\u817f\u8180\u8181\u8182\u8183\u8184\u8185\u8186\u8187\u8188\u8189\u818a\u818b\u818c\u818d\u818e\u818f\u8190\u8191\u8192\u8193\u8194\u8195\u8196\u8197\u8198\u8199\u819a\u819b\u819c\u819d\u819e\u819f\u81a0\u81a1\u81a2\u81a3\u81a4\u81a5\u81a6\u81a7\u81a8\u81a9\u81aa\u81ab\u81ac\u81ad\u81ae\u81af\u81b0\u81b1\u81b2\u81b3\u81b4\u81b5\u81b6\u81b7\u81b8\u81b9\u81ba\u81bb\u81bc\u81bd\u81be\u81bf\u81c0\u81c1\u81c2\u81c3\u81c4\u81c5\u81c6\u81c7\u81c8\u81c9\u81ca\u81cb\u81cc\u81cd\u81ce\u81cf\u81d0\u81d1\u81d2\u81d3\u81d4\u81d5\u81d6\u81d7\u81d8\u81d9\u81da\u81db\u81dc\u81dd\u81de\u81df\u81e0\u81e1\u81e2\u81e3\u81e4\u81e5\u81e6\u81e7\u81e8\u81e9\u81ea\u81eb\u81ec\u81ed\u81ee\u81ef\u81f0\u81f1\u81f2\u81f3\u81f4\u81f5\u81f6\u81f7\u81f8\u81f9\u81fa\u81fb\u81fc\u81fd\u81fe\u81ff\u8200\u8201\u8202\u8203\u8204\u8205\u8206\u8207\u8208\u8209\u820a\u820b\u820c\u820d\u820e\u820f\u8210\u8211\u8212\u8213\u8214\u8215\u8216\u8217\u8218\u8219\u821a\u821b\u821c\u821d\u821e\u821f\u8220\u8221\u8222\u8223\u8224\u8225\u8226\u8227\u8228\u8229\u822a\u822b\u822c\u822d\u822e\u822f\u8230\u8231\u8232\u8233\u8234\u8235\u8236\u8237\u8238\u8239\u823a\u823b\u823c\u823d\u823e\u823f\u8240\u8241\u8242\u8243\u8244\u8245\u8246\u8247\u8248\u8249\u824a\u824b\u824c\u824d\u824e\u824f\u8250\u8251\u8252\u8253\u8254\u8255\u8256\u8257\u8258\u8259\u825a\u825b\u825c\u825d\u825e\u825f\u8260\u8261\u8262\u8263\u8264\u8265\u8266\u8267\u8268\u8269\u826a\u826b\u826c\u826d\u826e\u826f\u8270\u8271\u8272\u8273\u8274\u8275\u8276\u8277\u8278\u8279\u827a\u827b\u827c\u827d\u827e\u827f\u8280\u8281\u8282\u8283\u8284\u8285\u8286\u8287\u8288\u8289\u828a\u828b\u828c\u828d\u828e\u828f\u8290\u8291\u8292\u8293\u8294\u8295\u8296\u8297\u8298\u8299\u829a\u829b\u829c\u829d\u829e\u829f\u82a0\u82a1\u82a2\u82a3\u82a4\u82a5\u82a6\u82a7\u82a8\u82a9\u82aa\u82ab\u82ac\u82ad\u82ae\u82af\u82b0\u82b1\u82b2\u82b3\u82b4\u82b5\u82b6\u82b7\u82b8\u82b9\u82ba\u82bb\u82bc\u82bd\u82be\u82bf\u82c0\u82c1\u82c2\u82c3\u82c4\u82c5\u82c6\u82c7\u82c8\u82c9\u82ca\u82cb\u82cc\u82cd\u82ce\u82cf\u82d0\u82d1\u82d2\u82d3\u82d4\u82d5\u82d6\u82d7\u82d8\u82d9\u82da\u82db\u82dc\u82dd\u82de\u82df\u82e0\u82e1\u82e2\u82e3\u82e4\u82e5\u82e6\u82e7\u82e8\u82e9\u82ea\u82eb\u82ec\u82ed\u82ee\u82ef\u82f0\u82f1\u82f2\u82f3\u82f4\u82f5\u82f6\u82f7\u82f8\u82f9\u82fa\u82fb\u82fc\u82fd\u82fe\u82ff\u8300\u8301\u8302\u8303\u8304\u8305\u8306\u8307\u8308\u8309\u830a\u830b\u830c\u830d\u830e\u830f\u8310\u8311\u8312\u8313\u8314\u8315\u8316\u8317\u8318\u8319\u831a\u831b\u831c\u831d\u831e\u831f\u8320\u8321\u8322\u8323\u8324\u8325\u8326\u8327\u8328\u8329\u832a\u832b\u832c\u832d\u832e\u832f\u8330\u8331\u8332\u8333\u8334\u8335\u8336\u8337\u8338\u8339\u833a\u833b\u833c\u833d\u833e\u833f\u8340\u8341\u8342\u8343\u8344\u8345\u8346\u8347\u8348\u8349\u834a\u834b\u834c\u834d\u834e\u834f\u8350\u8351\u8352\u8353\u8354\u8355\u8356\u8357\u8358\u8359\u835a\u835b\u835c\u835d\u835e\u835f\u8360\u8361\u8362\u8363\u8364\u8365\u8366\u8367\u8368\u8369\u836a\u836b\u836c\u836d\u836e\u836f\u8370\u8371\u8372\u8373\u8374\u8375\u8376\u8377\u8378\u8379\u837a\u837b\u837c\u837d\u837e\u837f\u8380\u8381\u8382\u8383\u8384\u8385\u8386\u8387\u8388\u8389\u838a\u838b\u838c\u838d\u838e\u838f\u8390\u8391\u8392\u8393\u8394\u8395\u8396\u8397\u8398\u8399\u839a\u839b\u839c\u839d\u839e\u839f\u83a0\u83a1\u83a2\u83a3\u83a4\u83a5\u83a6\u83a7\u83a8\u83a9\u83aa\u83ab\u83ac\u83ad\u83ae\u83af\u83b0\u83b1\u83b2\u83b3\u83b4\u83b5\u83b6\u83b7\u83b8\u83b9\u83ba\u83bb\u83bc\u83bd\u83be\u83bf\u83c0\u83c1\u83c2\u83c3\u83c4\u83c5\u83c6\u83c7\u83c8\u83c9\u83ca\u83cb\u83cc\u83cd\u83ce\u83cf\u83d0\u83d1\u83d2\u83d3\u83d4\u83d5\u83d6\u83d7\u83d8\u83d9\u83da\u83db\u83dc\u83dd\u83de\u83df\u83e0\u83e1\u83e2\u83e3\u83e4\u83e5\u83e6\u83e7\u83e8\u83e9\u83ea\u83eb\u83ec\u83ed\u83ee\u83ef\u83f0\u83f1\u83f2\u83f3\u83f4\u83f5\u83f6\u83f7\u83f8\u83f9\u83fa\u83fb\u83fc\u83fd\u83fe\u83ff\u8400\u8401\u8402\u8403\u8404\u8405\u8406\u8407\u8408\u8409\u840a\u840b\u840c\u840d\u840e\u840f\u8410\u8411\u8412\u8413\u8414\u8415\u8416\u8417\u8418\u8419\u841a\u841b\u841c\u841d\u841e\u841f\u8420\u8421\u8422\u8423\u8424\u8425\u8426\u8427\u8428\u8429\u842a\u842b\u842c\u842d\u842e\u842f\u8430\u8431\u8432\u8433\u8434\u8435\u8436\u8437\u8438\u8439\u843a\u843b\u843c\u843d\u843e\u843f\u8440\u8441\u8442\u8443\u8444\u8445\u8446\u8447\u8448\u8449\u844a\u844b\u844c\u844d\u844e\u844f\u8450\u8451\u8452\u8453\u8454\u8455\u8456\u8457\u8458\u8459\u845a\u845b\u845c\u845d\u845e\u845f\u8460\u8461\u8462\u8463\u8464\u8465\u8466\u8467\u8468\u8469\u846a\u846b\u846c\u846d\u846e\u846f\u8470\u8471\u8472\u8473\u8474\u8475\u8476\u8477\u8478\u8479\u847a\u847b\u847c\u847d\u847e\u847f\u8480\u8481\u8482\u8483\u8484\u8485\u8486\u8487\u8488\u8489\u848a\u848b\u848c\u848d\u848e\u848f\u8490\u8491\u8492\u8493\u8494\u8495\u8496\u8497\u8498\u8499\u849a\u849b\u849c\u849d\u849e\u849f\u84a0\u84a1\u84a2\u84a3\u84a4\u84a5\u84a6\u84a7\u84a8\u84a9\u84aa\u84ab\u84ac\u84ad\u84ae\u84af\u84b0\u84b1\u84b2\u84b3\u84b4\u84b5\u84b6\u84b7\u84b8\u84b9\u84ba\u84bb\u84bc\u84bd\u84be\u84bf\u84c0\u84c1\u84c2\u84c3\u84c4\u84c5\u84c6\u84c7\u84c8\u84c9\u84ca\u84cb\u84cc\u84cd\u84ce\u84cf\u84d0\u84d1\u84d2\u84d3\u84d4\u84d5\u84d6\u84d7\u84d8\u84d9\u84da\u84db\u84dc\u84dd\u84de\u84df\u84e0\u84e1\u84e2\u84e3\u84e4\u84e5\u84e6\u84e7\u84e8\u84e9\u84ea\u84eb\u84ec\u84ed\u84ee\u84ef\u84f0\u84f1\u84f2\u84f3\u84f4\u84f5\u84f6\u84f7\u84f8\u84f9\u84fa\u84fb\u84fc\u84fd\u84fe\u84ff\u8500\u8501\u8502\u8503\u8504\u8505\u8506\u8507\u8508\u8509\u850a\u850b\u850c\u850d\u850e\u850f\u8510\u8511\u8512\u8513\u8514\u8515\u8516\u8517\u8518\u8519\u851a\u851b\u851c\u851d\u851e\u851f\u8520\u8521\u8522\u8523\u8524\u8525\u8526\u8527\u8528\u8529\u852a\u852b\u852c\u852d\u852e\u852f\u8530\u8531\u8532\u8533\u8534\u8535\u8536\u8537\u8538\u8539\u853a\u853b\u853c\u853d\u853e\u853f\u8540\u8541\u8542\u8543\u8544\u8545\u8546\u8547\u8548\u8549\u854a\u854b\u854c\u854d\u854e\u854f\u8550\u8551\u8552\u8553\u8554\u8555\u8556\u8557\u8558\u8559\u855a\u855b\u855c\u855d\u855e\u855f\u8560\u8561\u8562\u8563\u8564\u8565\u8566\u8567\u8568\u8569\u856a\u856b\u856c\u856d\u856e\u856f\u8570\u8571\u8572\u8573\u8574\u8575\u8576\u8577\u8578\u8579\u857a\u857b\u857c\u857d\u857e\u857f\u8580\u8581\u8582\u8583\u8584\u8585\u8586\u8587\u8588\u8589\u858a\u858b\u858c\u858d\u858e\u858f\u8590\u8591\u8592\u8593\u8594\u8595\u8596\u8597\u8598\u8599\u859a\u859b\u859c\u859d\u859e\u859f\u85a0\u85a1\u85a2\u85a3\u85a4\u85a5\u85a6\u85a7\u85a8\u85a9\u85aa\u85ab\u85ac\u85ad\u85ae\u85af\u85b0\u85b1\u85b2\u85b3\u85b4\u85b5\u85b6\u85b7\u85b8\u85b9\u85ba\u85bb\u85bc\u85bd\u85be\u85bf\u85c0\u85c1\u85c2\u85c3\u85c4\u85c5\u85c6\u85c7\u85c8\u85c9\u85ca\u85cb\u85cc\u85cd\u85ce\u85cf\u85d0\u85d1\u85d2\u85d3\u85d4\u85d5\u85d6\u85d7\u85d8\u85d9\u85da\u85db\u85dc\u85dd\u85de\u85df\u85e0\u85e1\u85e2\u85e3\u85e4\u85e5\u85e6\u85e7\u85e8\u85e9\u85ea\u85eb\u85ec\u85ed\u85ee\u85ef\u85f0\u85f1\u85f2\u85f3\u85f4\u85f5\u85f6\u85f7\u85f8\u85f9\u85fa\u85fb\u85fc\u85fd\u85fe\u85ff\u8600\u8601\u8602\u8603\u8604\u8605\u8606\u8607\u8608\u8609\u860a\u860b\u860c\u860d\u860e\u860f\u8610\u8611\u8612\u8613\u8614\u8615\u8616\u8617\u8618\u8619\u861a\u861b\u861c\u861d\u861e\u861f\u8620\u8621\u8622\u8623\u8624\u8625\u8626\u8627\u8628\u8629\u862a\u862b\u862c\u862d\u862e\u862f\u8630\u8631\u8632\u8633\u8634\u8635\u8636\u8637\u8638\u8639\u863a\u863b\u863c\u863d\u863e\u863f\u8640\u8641\u8642\u8643\u8644\u8645\u8646\u8647\u8648\u8649\u864a\u864b\u864c\u864d\u864e\u864f\u8650\u8651\u8652\u8653\u8654\u8655\u8656\u8657\u8658\u8659\u865a\u865b\u865c\u865d\u865e\u865f\u8660\u8661\u8662\u8663\u8664\u8665\u8666\u8667\u8668\u8669\u866a\u866b\u866c\u866d\u866e\u866f\u8670\u8671\u8672\u8673\u8674\u8675\u8676\u8677\u8678\u8679\u867a\u867b\u867c\u867d\u867e\u867f\u8680\u8681\u8682\u8683\u8684\u8685\u8686\u8687\u8688\u8689\u868a\u868b\u868c\u868d\u868e\u868f\u8690\u8691\u8692\u8693\u8694\u8695\u8696\u8697\u8698\u8699\u869a\u869b\u869c\u869d\u869e\u869f\u86a0\u86a1\u86a2\u86a3\u86a4\u86a5\u86a6\u86a7\u86a8\u86a9\u86aa\u86ab\u86ac\u86ad\u86ae\u86af\u86b0\u86b1\u86b2\u86b3\u86b4\u86b5\u86b6\u86b7\u86b8\u86b9\u86ba\u86bb\u86bc\u86bd\u86be\u86bf\u86c0\u86c1\u86c2\u86c3\u86c4\u86c5\u86c6\u86c7\u86c8\u86c9\u86ca\u86cb\u86cc\u86cd\u86ce\u86cf\u86d0\u86d1\u86d2\u86d3\u86d4\u86d5\u86d6\u86d7\u86d8\u86d9\u86da\u86db\u86dc\u86dd\u86de\u86df\u86e0\u86e1\u86e2\u86e3\u86e4\u86e5\u86e6\u86e7\u86e8\u86e9\u86ea\u86eb\u86ec\u86ed\u86ee\u86ef\u86f0\u86f1\u86f2\u86f3\u86f4\u86f5\u86f6\u86f7\u86f8\u86f9\u86fa\u86fb\u86fc\u86fd\u86fe\u86ff\u8700\u8701\u8702\u8703\u8704\u8705\u8706\u8707\u8708\u8709\u870a\u870b\u870c\u870d\u870e\u870f\u8710\u8711\u8712\u8713\u8714\u8715\u8716\u8717\u8718\u8719\u871a\u871b\u871c\u871d\u871e\u871f\u8720\u8721\u8722\u8723\u8724\u8725\u8726\u8727\u8728\u8729\u872a\u872b\u872c\u872d\u872e\u872f\u8730\u8731\u8732\u8733\u8734\u8735\u8736\u8737\u8738\u8739\u873a\u873b\u873c\u873d\u873e\u873f\u8740\u8741\u8742\u8743\u8744\u8745\u8746\u8747\u8748\u8749\u874a\u874b\u874c\u874d\u874e\u874f\u8750\u8751\u8752\u8753\u8754\u8755\u8756\u8757\u8758\u8759\u875a\u875b\u875c\u875d\u875e\u875f\u8760\u8761\u8762\u8763\u8764\u8765\u8766\u8767\u8768\u8769\u876a\u876b\u876c\u876d\u876e\u876f\u8770\u8771\u8772\u8773\u8774\u8775\u8776\u8777\u8778\u8779\u877a\u877b\u877c\u877d\u877e\u877f\u8780\u8781\u8782\u8783\u8784\u8785\u8786\u8787\u8788\u8789\u878a\u878b\u878c\u878d\u878e\u878f\u8790\u8791\u8792\u8793\u8794\u8795\u8796\u8797\u8798\u8799\u879a\u879b\u879c\u879d\u879e\u879f\u87a0\u87a1\u87a2\u87a3\u87a4\u87a5\u87a6\u87a7\u87a8\u87a9\u87aa\u87ab\u87ac\u87ad\u87ae\u87af\u87b0\u87b1\u87b2\u87b3\u87b4\u87b5\u87b6\u87b7\u87b8\u87b9\u87ba\u87bb\u87bc\u87bd\u87be\u87bf\u87c0\u87c1\u87c2\u87c3\u87c4\u87c5\u87c6\u87c7\u87c8\u87c9\u87ca\u87cb\u87cc\u87cd\u87ce\u87cf\u87d0\u87d1\u87d2\u87d3\u87d4\u87d5\u87d6\u87d7\u87d8\u87d9\u87da\u87db\u87dc\u87dd\u87de\u87df\u87e0\u87e1\u87e2\u87e3\u87e4\u87e5\u87e6\u87e7\u87e8\u87e9\u87ea\u87eb\u87ec\u87ed\u87ee\u87ef\u87f0\u87f1\u87f2\u87f3\u87f4\u87f5\u87f6\u87f7\u87f8\u87f9\u87fa\u87fb\u87fc\u87fd\u87fe\u87ff\u8800\u8801\u8802\u8803\u8804\u8805\u8806\u8807\u8808\u8809\u880a\u880b\u880c\u880d\u880e\u880f\u8810\u8811\u8812\u8813\u8814\u8815\u8816\u8817\u8818\u8819\u881a\u881b\u881c\u881d\u881e\u881f\u8820\u8821\u8822\u8823\u8824\u8825\u8826\u8827\u8828\u8829\u882a\u882b\u882c\u882d\u882e\u882f\u8830\u8831\u8832\u8833\u8834\u8835\u8836\u8837\u8838\u8839\u883a\u883b\u883c\u883d\u883e\u883f\u8840\u8841\u8842\u8843\u8844\u8845\u8846\u8847\u8848\u8849\u884a\u884b\u884c\u884d\u884e\u884f\u8850\u8851\u8852\u8853\u8854\u8855\u8856\u8857\u8858\u8859\u885a\u885b\u885c\u885d\u885e\u885f\u8860\u8861\u8862\u8863\u8864\u8865\u8866\u8867\u8868\u8869\u886a\u886b\u886c\u886d\u886e\u886f\u8870\u8871\u8872\u8873\u8874\u8875\u8876\u8877\u8878\u8879\u887a\u887b\u887c\u887d\u887e\u887f\u8880\u8881\u8882\u8883\u8884\u8885\u8886\u8887\u8888\u8889\u888a\u888b\u888c\u888d\u888e\u888f\u8890\u8891\u8892\u8893\u8894\u8895\u8896\u8897\u8898\u8899\u889a\u889b\u889c\u889d\u889e\u889f\u88a0\u88a1\u88a2\u88a3\u88a4\u88a5\u88a6\u88a7\u88a8\u88a9\u88aa\u88ab\u88ac\u88ad\u88ae\u88af\u88b0\u88b1\u88b2\u88b3\u88b4\u88b5\u88b6\u88b7\u88b8\u88b9\u88ba\u88bb\u88bc\u88bd\u88be\u88bf\u88c0\u88c1\u88c2\u88c3\u88c4\u88c5\u88c6\u88c7\u88c8\u88c9\u88ca\u88cb\u88cc\u88cd\u88ce\u88cf\u88d0\u88d1\u88d2\u88d3\u88d4\u88d5\u88d6\u88d7\u88d8\u88d9\u88da\u88db\u88dc\u88dd\u88de\u88df\u88e0\u88e1\u88e2\u88e3\u88e4\u88e5\u88e6\u88e7\u88e8\u88e9\u88ea\u88eb\u88ec\u88ed\u88ee\u88ef\u88f0\u88f1\u88f2\u88f3\u88f4\u88f5\u88f6\u88f7\u88f8\u88f9\u88fa\u88fb\u88fc\u88fd\u88fe\u88ff\u8900\u8901\u8902\u8903\u8904\u8905\u8906\u8907\u8908\u8909\u890a\u890b\u890c\u890d\u890e\u890f\u8910\u8911\u8912\u8913\u8914\u8915\u8916\u8917\u8918\u8919\u891a\u891b\u891c\u891d\u891e\u891f\u8920\u8921\u8922\u8923\u8924\u8925\u8926\u8927\u8928\u8929\u892a\u892b\u892c\u892d\u892e\u892f\u8930\u8931\u8932\u8933\u8934\u8935\u8936\u8937\u8938\u8939\u893a\u893b\u893c\u893d\u893e\u893f\u8940\u8941\u8942\u8943\u8944\u8945\u8946\u8947\u8948\u8949\u894a\u894b\u894c\u894d\u894e\u894f\u8950\u8951\u8952\u8953\u8954\u8955\u8956\u8957\u8958\u8959\u895a\u895b\u895c\u895d\u895e\u895f\u8960\u8961\u8962\u8963\u8964\u8965\u8966\u8967\u8968\u8969\u896a\u896b\u896c\u896d\u896e\u896f\u8970\u8971\u8972\u8973\u8974\u8975\u8976\u8977\u8978\u8979\u897a\u897b\u897c\u897d\u897e\u897f\u8980\u8981\u8982\u8983\u8984\u8985\u8986\u8987\u8988\u8989\u898a\u898b\u898c\u898d\u898e\u898f\u8990\u8991\u8992\u8993\u8994\u8995\u8996\u8997\u8998\u8999\u899a\u899b\u899c\u899d\u899e\u899f\u89a0\u89a1\u89a2\u89a3\u89a4\u89a5\u89a6\u89a7\u89a8\u89a9\u89aa\u89ab\u89ac\u89ad\u89ae\u89af\u89b0\u89b1\u89b2\u89b3\u89b4\u89b5\u89b6\u89b7\u89b8\u89b9\u89ba\u89bb\u89bc\u89bd\u89be\u89bf\u89c0\u89c1\u89c2\u89c3\u89c4\u89c5\u89c6\u89c7\u89c8\u89c9\u89ca\u89cb\u89cc\u89cd\u89ce\u89cf\u89d0\u89d1\u89d2\u89d3\u89d4\u89d5\u89d6\u89d7\u89d8\u89d9\u89da\u89db\u89dc\u89dd\u89de\u89df\u89e0\u89e1\u89e2\u89e3\u89e4\u89e5\u89e6\u89e7\u89e8\u89e9\u89ea\u89eb\u89ec\u89ed\u89ee\u89ef\u89f0\u89f1\u89f2\u89f3\u89f4\u89f5\u89f6\u89f7\u89f8\u89f9\u89fa\u89fb\u89fc\u89fd\u89fe\u89ff\u8a00\u8a01\u8a02\u8a03\u8a04\u8a05\u8a06\u8a07\u8a08\u8a09\u8a0a\u8a0b\u8a0c\u8a0d\u8a0e\u8a0f\u8a10\u8a11\u8a12\u8a13\u8a14\u8a15\u8a16\u8a17\u8a18\u8a19\u8a1a\u8a1b\u8a1c\u8a1d\u8a1e\u8a1f\u8a20\u8a21\u8a22\u8a23\u8a24\u8a25\u8a26\u8a27\u8a28\u8a29\u8a2a\u8a2b\u8a2c\u8a2d\u8a2e\u8a2f\u8a30\u8a31\u8a32\u8a33\u8a34\u8a35\u8a36\u8a37\u8a38\u8a39\u8a3a\u8a3b\u8a3c\u8a3d\u8a3e\u8a3f\u8a40\u8a41\u8a42\u8a43\u8a44\u8a45\u8a46\u8a47\u8a48\u8a49\u8a4a\u8a4b\u8a4c\u8a4d\u8a4e\u8a4f\u8a50\u8a51\u8a52\u8a53\u8a54\u8a55\u8a56\u8a57\u8a58\u8a59\u8a5a\u8a5b\u8a5c\u8a5d\u8a5e\u8a5f\u8a60\u8a61\u8a62\u8a63\u8a64\u8a65\u8a66\u8a67\u8a68\u8a69\u8a6a\u8a6b\u8a6c\u8a6d\u8a6e\u8a6f\u8a70\u8a71\u8a72\u8a73\u8a74\u8a75\u8a76\u8a77\u8a78\u8a79\u8a7a\u8a7b\u8a7c\u8a7d\u8a7e\u8a7f\u8a80\u8a81\u8a82\u8a83\u8a84\u8a85\u8a86\u8a87\u8a88\u8a89\u8a8a\u8a8b\u8a8c\u8a8d\u8a8e\u8a8f\u8a90\u8a91\u8a92\u8a93\u8a94\u8a95\u8a96\u8a97\u8a98\u8a99\u8a9a\u8a9b\u8a9c\u8a9d\u8a9e\u8a9f\u8aa0\u8aa1\u8aa2\u8aa3\u8aa4\u8aa5\u8aa6\u8aa7\u8aa8\u8aa9\u8aaa\u8aab\u8aac\u8aad\u8aae\u8aaf\u8ab0\u8ab1\u8ab2\u8ab3\u8ab4\u8ab5\u8ab6\u8ab7\u8ab8\u8ab9\u8aba\u8abb\u8abc\u8abd\u8abe\u8abf\u8ac0\u8ac1\u8ac2\u8ac3\u8ac4\u8ac5\u8ac6\u8ac7\u8ac8\u8ac9\u8aca\u8acb\u8acc\u8acd\u8ace\u8acf\u8ad0\u8ad1\u8ad2\u8ad3\u8ad4\u8ad5\u8ad6\u8ad7\u8ad8\u8ad9\u8ada\u8adb\u8adc\u8add\u8ade\u8adf\u8ae0\u8ae1\u8ae2\u8ae3\u8ae4\u8ae5\u8ae6\u8ae7\u8ae8\u8ae9\u8aea\u8aeb\u8aec\u8aed\u8aee\u8aef\u8af0\u8af1\u8af2\u8af3\u8af4\u8af5\u8af6\u8af7\u8af8\u8af9\u8afa\u8afb\u8afc\u8afd\u8afe\u8aff\u8b00\u8b01\u8b02\u8b03\u8b04\u8b05\u8b06\u8b07\u8b08\u8b09\u8b0a\u8b0b\u8b0c\u8b0d\u8b0e\u8b0f\u8b10\u8b11\u8b12\u8b13\u8b14\u8b15\u8b16\u8b17\u8b18\u8b19\u8b1a\u8b1b\u8b1c\u8b1d\u8b1e\u8b1f\u8b20\u8b21\u8b22\u8b23\u8b24\u8b25\u8b26\u8b27\u8b28\u8b29\u8b2a\u8b2b\u8b2c\u8b2d\u8b2e\u8b2f\u8b30\u8b31\u8b32\u8b33\u8b34\u8b35\u8b36\u8b37\u8b38\u8b39\u8b3a\u8b3b\u8b3c\u8b3d\u8b3e\u8b3f\u8b40\u8b41\u8b42\u8b43\u8b44\u8b45\u8b46\u8b47\u8b48\u8b49\u8b4a\u8b4b\u8b4c\u8b4d\u8b4e\u8b4f\u8b50\u8b51\u8b52\u8b53\u8b54\u8b55\u8b56\u8b57\u8b58\u8b59\u8b5a\u8b5b\u8b5c\u8b5d\u8b5e\u8b5f\u8b60\u8b61\u8b62\u8b63\u8b64\u8b65\u8b66\u8b67\u8b68\u8b69\u8b6a\u8b6b\u8b6c\u8b6d\u8b6e\u8b6f\u8b70\u8b71\u8b72\u8b73\u8b74\u8b75\u8b76\u8b77\u8b78\u8b79\u8b7a\u8b7b\u8b7c\u8b7d\u8b7e\u8b7f\u8b80\u8b81\u8b82\u8b83\u8b84\u8b85\u8b86\u8b87\u8b88\u8b89\u8b8a\u8b8b\u8b8c\u8b8d\u8b8e\u8b8f\u8b90\u8b91\u8b92\u8b93\u8b94\u8b95\u8b96\u8b97\u8b98\u8b99\u8b9a\u8b9b\u8b9c\u8b9d\u8b9e\u8b9f\u8ba0\u8ba1\u8ba2\u8ba3\u8ba4\u8ba5\u8ba6\u8ba7\u8ba8\u8ba9\u8baa\u8bab\u8bac\u8bad\u8bae\u8baf\u8bb0\u8bb1\u8bb2\u8bb3\u8bb4\u8bb5\u8bb6\u8bb7\u8bb8\u8bb9\u8bba\u8bbb\u8bbc\u8bbd\u8bbe\u8bbf\u8bc0\u8bc1\u8bc2\u8bc3\u8bc4\u8bc5\u8bc6\u8bc7\u8bc8\u8bc9\u8bca\u8bcb\u8bcc\u8bcd\u8bce\u8bcf\u8bd0\u8bd1\u8bd2\u8bd3\u8bd4\u8bd5\u8bd6\u8bd7\u8bd8\u8bd9\u8bda\u8bdb\u8bdc\u8bdd\u8bde\u8bdf\u8be0\u8be1\u8be2\u8be3\u8be4\u8be5\u8be6\u8be7\u8be8\u8be9\u8bea\u8beb\u8bec\u8bed\u8bee\u8bef\u8bf0\u8bf1\u8bf2\u8bf3\u8bf4\u8bf5\u8bf6\u8bf7\u8bf8\u8bf9\u8bfa\u8bfb\u8bfc\u8bfd\u8bfe\u8bff\u8c00\u8c01\u8c02\u8c03\u8c04\u8c05\u8c06\u8c07\u8c08\u8c09\u8c0a\u8c0b\u8c0c\u8c0d\u8c0e\u8c0f\u8c10\u8c11\u8c12\u8c13\u8c14\u8c15\u8c16\u8c17\u8c18\u8c19\u8c1a\u8c1b\u8c1c\u8c1d\u8c1e\u8c1f\u8c20\u8c21\u8c22\u8c23\u8c24\u8c25\u8c26\u8c27\u8c28\u8c29\u8c2a\u8c2b\u8c2c\u8c2d\u8c2e\u8c2f\u8c30\u8c31\u8c32\u8c33\u8c34\u8c35\u8c36\u8c37\u8c38\u8c39\u8c3a\u8c3b\u8c3c\u8c3d\u8c3e\u8c3f\u8c40\u8c41\u8c42\u8c43\u8c44\u8c45\u8c46\u8c47\u8c48\u8c49\u8c4a\u8c4b\u8c4c\u8c4d\u8c4e\u8c4f\u8c50\u8c51\u8c52\u8c53\u8c54\u8c55\u8c56\u8c57\u8c58\u8c59\u8c5a\u8c5b\u8c5c\u8c5d\u8c5e\u8c5f\u8c60\u8c61\u8c62\u8c63\u8c64\u8c65\u8c66\u8c67\u8c68\u8c69\u8c6a\u8c6b\u8c6c\u8c6d\u8c6e\u8c6f\u8c70\u8c71\u8c72\u8c73\u8c74\u8c75\u8c76\u8c77\u8c78\u8c79\u8c7a\u8c7b\u8c7c\u8c7d\u8c7e\u8c7f\u8c80\u8c81\u8c82\u8c83\u8c84\u8c85\u8c86\u8c87\u8c88\u8c89\u8c8a\u8c8b\u8c8c\u8c8d\u8c8e\u8c8f\u8c90\u8c91\u8c92\u8c93\u8c94\u8c95\u8c96\u8c97\u8c98\u8c99\u8c9a\u8c9b\u8c9c\u8c9d\u8c9e\u8c9f\u8ca0\u8ca1\u8ca2\u8ca3\u8ca4\u8ca5\u8ca6\u8ca7\u8ca8\u8ca9\u8caa\u8cab\u8cac\u8cad\u8cae\u8caf\u8cb0\u8cb1\u8cb2\u8cb3\u8cb4\u8cb5\u8cb6\u8cb7\u8cb8\u8cb9\u8cba\u8cbb\u8cbc\u8cbd\u8cbe\u8cbf\u8cc0\u8cc1\u8cc2\u8cc3\u8cc4\u8cc5\u8cc6\u8cc7\u8cc8\u8cc9\u8cca\u8ccb\u8ccc\u8ccd\u8cce\u8ccf\u8cd0\u8cd1\u8cd2\u8cd3\u8cd4\u8cd5\u8cd6\u8cd7\u8cd8\u8cd9\u8cda\u8cdb\u8cdc\u8cdd\u8cde\u8cdf\u8ce0\u8ce1\u8ce2\u8ce3\u8ce4\u8ce5\u8ce6\u8ce7\u8ce8\u8ce9\u8cea\u8ceb\u8cec\u8ced\u8cee\u8cef\u8cf0\u8cf1\u8cf2\u8cf3\u8cf4\u8cf5\u8cf6\u8cf7\u8cf8\u8cf9\u8cfa\u8cfb\u8cfc\u8cfd\u8cfe\u8cff\u8d00\u8d01\u8d02\u8d03\u8d04\u8d05\u8d06\u8d07\u8d08\u8d09\u8d0a\u8d0b\u8d0c\u8d0d\u8d0e\u8d0f\u8d10\u8d11\u8d12\u8d13\u8d14\u8d15\u8d16\u8d17\u8d18\u8d19\u8d1a\u8d1b\u8d1c\u8d1d\u8d1e\u8d1f\u8d20\u8d21\u8d22\u8d23\u8d24\u8d25\u8d26\u8d27\u8d28\u8d29\u8d2a\u8d2b\u8d2c\u8d2d\u8d2e\u8d2f\u8d30\u8d31\u8d32\u8d33\u8d34\u8d35\u8d36\u8d37\u8d38\u8d39\u8d3a\u8d3b\u8d3c\u8d3d\u8d3e\u8d3f\u8d40\u8d41\u8d42\u8d43\u8d44\u8d45\u8d46\u8d47\u8d48\u8d49\u8d4a\u8d4b\u8d4c\u8d4d\u8d4e\u8d4f\u8d50\u8d51\u8d52\u8d53\u8d54\u8d55\u8d56\u8d57\u8d58\u8d59\u8d5a\u8d5b\u8d5c\u8d5d\u8d5e\u8d5f\u8d60\u8d61\u8d62\u8d63\u8d64\u8d65\u8d66\u8d67\u8d68\u8d69\u8d6a\u8d6b\u8d6c\u8d6d\u8d6e\u8d6f\u8d70\u8d71\u8d72\u8d73\u8d74\u8d75\u8d76\u8d77\u8d78\u8d79\u8d7a\u8d7b\u8d7c\u8d7d\u8d7e\u8d7f\u8d80\u8d81\u8d82\u8d83\u8d84\u8d85\u8d86\u8d87\u8d88\u8d89\u8d8a\u8d8b\u8d8c\u8d8d\u8d8e\u8d8f\u8d90\u8d91\u8d92\u8d93\u8d94\u8d95\u8d96\u8d97\u8d98\u8d99\u8d9a\u8d9b\u8d9c\u8d9d\u8d9e\u8d9f\u8da0\u8da1\u8da2\u8da3\u8da4\u8da5\u8da6\u8da7\u8da8\u8da9\u8daa\u8dab\u8dac\u8dad\u8dae\u8daf\u8db0\u8db1\u8db2\u8db3\u8db4\u8db5\u8db6\u8db7\u8db8\u8db9\u8dba\u8dbb\u8dbc\u8dbd\u8dbe\u8dbf\u8dc0\u8dc1\u8dc2\u8dc3\u8dc4\u8dc5\u8dc6\u8dc7\u8dc8\u8dc9\u8dca\u8dcb\u8dcc\u8dcd\u8dce\u8dcf\u8dd0\u8dd1\u8dd2\u8dd3\u8dd4\u8dd5\u8dd6\u8dd7\u8dd8\u8dd9\u8dda\u8ddb\u8ddc\u8ddd\u8dde\u8ddf\u8de0\u8de1\u8de2\u8de3\u8de4\u8de5\u8de6\u8de7\u8de8\u8de9\u8dea\u8deb\u8dec\u8ded\u8dee\u8def\u8df0\u8df1\u8df2\u8df3\u8df4\u8df5\u8df6\u8df7\u8df8\u8df9\u8dfa\u8dfb\u8dfc\u8dfd\u8dfe\u8dff\u8e00\u8e01\u8e02\u8e03\u8e04\u8e05\u8e06\u8e07\u8e08\u8e09\u8e0a\u8e0b\u8e0c\u8e0d\u8e0e\u8e0f\u8e10\u8e11\u8e12\u8e13\u8e14\u8e15\u8e16\u8e17\u8e18\u8e19\u8e1a\u8e1b\u8e1c\u8e1d\u8e1e\u8e1f\u8e20\u8e21\u8e22\u8e23\u8e24\u8e25\u8e26\u8e27\u8e28\u8e29\u8e2a\u8e2b\u8e2c\u8e2d\u8e2e\u8e2f\u8e30\u8e31\u8e32\u8e33\u8e34\u8e35\u8e36\u8e37\u8e38\u8e39\u8e3a\u8e3b\u8e3c\u8e3d\u8e3e\u8e3f\u8e40\u8e41\u8e42\u8e43\u8e44\u8e45\u8e46\u8e47\u8e48\u8e49\u8e4a\u8e4b\u8e4c\u8e4d\u8e4e\u8e4f\u8e50\u8e51\u8e52\u8e53\u8e54\u8e55\u8e56\u8e57\u8e58\u8e59\u8e5a\u8e5b\u8e5c\u8e5d\u8e5e\u8e5f\u8e60\u8e61\u8e62\u8e63\u8e64\u8e65\u8e66\u8e67\u8e68\u8e69\u8e6a\u8e6b\u8e6c\u8e6d\u8e6e\u8e6f\u8e70\u8e71\u8e72\u8e73\u8e74\u8e75\u8e76\u8e77\u8e78\u8e79\u8e7a\u8e7b\u8e7c\u8e7d\u8e7e\u8e7f\u8e80\u8e81\u8e82\u8e83\u8e84\u8e85\u8e86\u8e87\u8e88\u8e89\u8e8a\u8e8b\u8e8c\u8e8d\u8e8e\u8e8f\u8e90\u8e91\u8e92\u8e93\u8e94\u8e95\u8e96\u8e97\u8e98\u8e99\u8e9a\u8e9b\u8e9c\u8e9d\u8e9e\u8e9f\u8ea0\u8ea1\u8ea2\u8ea3\u8ea4\u8ea5\u8ea6\u8ea7\u8ea8\u8ea9\u8eaa\u8eab\u8eac\u8ead\u8eae\u8eaf\u8eb0\u8eb1\u8eb2\u8eb3\u8eb4\u8eb5\u8eb6\u8eb7\u8eb8\u8eb9\u8eba\u8ebb\u8ebc\u8ebd\u8ebe\u8ebf\u8ec0\u8ec1\u8ec2\u8ec3\u8ec4\u8ec5\u8ec6\u8ec7\u8ec8\u8ec9\u8eca\u8ecb\u8ecc\u8ecd\u8ece\u8ecf\u8ed0\u8ed1\u8ed2\u8ed3\u8ed4\u8ed5\u8ed6\u8ed7\u8ed8\u8ed9\u8eda\u8edb\u8edc\u8edd\u8ede\u8edf\u8ee0\u8ee1\u8ee2\u8ee3\u8ee4\u8ee5\u8ee6\u8ee7\u8ee8\u8ee9\u8eea\u8eeb\u8eec\u8eed\u8eee\u8eef\u8ef0\u8ef1\u8ef2\u8ef3\u8ef4\u8ef5\u8ef6\u8ef7\u8ef8\u8ef9\u8efa\u8efb\u8efc\u8efd\u8efe\u8eff\u8f00\u8f01\u8f02\u8f03\u8f04\u8f05\u8f06\u8f07\u8f08\u8f09\u8f0a\u8f0b\u8f0c\u8f0d\u8f0e\u8f0f\u8f10\u8f11\u8f12\u8f13\u8f14\u8f15\u8f16\u8f17\u8f18\u8f19\u8f1a\u8f1b\u8f1c\u8f1d\u8f1e\u8f1f\u8f20\u8f21\u8f22\u8f23\u8f24\u8f25\u8f26\u8f27\u8f28\u8f29\u8f2a\u8f2b\u8f2c\u8f2d\u8f2e\u8f2f\u8f30\u8f31\u8f32\u8f33\u8f34\u8f35\u8f36\u8f37\u8f38\u8f39\u8f3a\u8f3b\u8f3c\u8f3d\u8f3e\u8f3f\u8f40\u8f41\u8f42\u8f43\u8f44\u8f45\u8f46\u8f47\u8f48\u8f49\u8f4a\u8f4b\u8f4c\u8f4d\u8f4e\u8f4f\u8f50\u8f51\u8f52\u8f53\u8f54\u8f55\u8f56\u8f57\u8f58\u8f59\u8f5a\u8f5b\u8f5c\u8f5d\u8f5e\u8f5f\u8f60\u8f61\u8f62\u8f63\u8f64\u8f65\u8f66\u8f67\u8f68\u8f69\u8f6a\u8f6b\u8f6c\u8f6d\u8f6e\u8f6f\u8f70\u8f71\u8f72\u8f73\u8f74\u8f75\u8f76\u8f77\u8f78\u8f79\u8f7a\u8f7b\u8f7c\u8f7d\u8f7e\u8f7f\u8f80\u8f81\u8f82\u8f83\u8f84\u8f85\u8f86\u8f87\u8f88\u8f89\u8f8a\u8f8b\u8f8c\u8f8d\u8f8e\u8f8f\u8f90\u8f91\u8f92\u8f93\u8f94\u8f95\u8f96\u8f97\u8f98\u8f99\u8f9a\u8f9b\u8f9c\u8f9d\u8f9e\u8f9f\u8fa0\u8fa1\u8fa2\u8fa3\u8fa4\u8fa5\u8fa6\u8fa7\u8fa8\u8fa9\u8faa\u8fab\u8fac\u8fad\u8fae\u8faf\u8fb0\u8fb1\u8fb2\u8fb3\u8fb4\u8fb5\u8fb6\u8fb7\u8fb8\u8fb9\u8fba\u8fbb\u8fbc\u8fbd\u8fbe\u8fbf\u8fc0\u8fc1\u8fc2\u8fc3\u8fc4\u8fc5\u8fc6\u8fc7\u8fc8\u8fc9\u8fca\u8fcb\u8fcc\u8fcd\u8fce\u8fcf\u8fd0\u8fd1\u8fd2\u8fd3\u8fd4\u8fd5\u8fd6\u8fd7\u8fd8\u8fd9\u8fda\u8fdb\u8fdc\u8fdd\u8fde\u8fdf\u8fe0\u8fe1\u8fe2\u8fe3\u8fe4\u8fe5\u8fe6\u8fe7\u8fe8\u8fe9\u8fea\u8feb\u8fec\u8fed\u8fee\u8fef\u8ff0\u8ff1\u8ff2\u8ff3\u8ff4\u8ff5\u8ff6\u8ff7\u8ff8\u8ff9\u8ffa\u8ffb\u8ffc\u8ffd\u8ffe\u8fff\u9000\u9001\u9002\u9003\u9004\u9005\u9006\u9007\u9008\u9009\u900a\u900b\u900c\u900d\u900e\u900f\u9010\u9011\u9012\u9013\u9014\u9015\u9016\u9017\u9018\u9019\u901a\u901b\u901c\u901d\u901e\u901f\u9020\u9021\u9022\u9023\u9024\u9025\u9026\u9027\u9028\u9029\u902a\u902b\u902c\u902d\u902e\u902f\u9030\u9031\u9032\u9033\u9034\u9035\u9036\u9037\u9038\u9039\u903a\u903b\u903c\u903d\u903e\u903f\u9040\u9041\u9042\u9043\u9044\u9045\u9046\u9047\u9048\u9049\u904a\u904b\u904c\u904d\u904e\u904f\u9050\u9051\u9052\u9053\u9054\u9055\u9056\u9057\u9058\u9059\u905a\u905b\u905c\u905d\u905e\u905f\u9060\u9061\u9062\u9063\u9064\u9065\u9066\u9067\u9068\u9069\u906a\u906b\u906c\u906d\u906e\u906f\u9070\u9071\u9072\u9073\u9074\u9075\u9076\u9077\u9078\u9079\u907a\u907b\u907c\u907d\u907e\u907f\u9080\u9081\u9082\u9083\u9084\u9085\u9086\u9087\u9088\u9089\u908a\u908b\u908c\u908d\u908e\u908f\u9090\u9091\u9092\u9093\u9094\u9095\u9096\u9097\u9098\u9099\u909a\u909b\u909c\u909d\u909e\u909f\u90a0\u90a1\u90a2\u90a3\u90a4\u90a5\u90a6\u90a7\u90a8\u90a9\u90aa\u90ab\u90ac\u90ad\u90ae\u90af\u90b0\u90b1\u90b2\u90b3\u90b4\u90b5\u90b6\u90b7\u90b8\u90b9\u90ba\u90bb\u90bc\u90bd\u90be\u90bf\u90c0\u90c1\u90c2\u90c3\u90c4\u90c5\u90c6\u90c7\u90c8\u90c9\u90ca\u90cb\u90cc\u90cd\u90ce\u90cf\u90d0\u90d1\u90d2\u90d3\u90d4\u90d5\u90d6\u90d7\u90d8\u90d9\u90da\u90db\u90dc\u90dd\u90de\u90df\u90e0\u90e1\u90e2\u90e3\u90e4\u90e5\u90e6\u90e7\u90e8\u90e9\u90ea\u90eb\u90ec\u90ed\u90ee\u90ef\u90f0\u90f1\u90f2\u90f3\u90f4\u90f5\u90f6\u90f7\u90f8\u90f9\u90fa\u90fb\u90fc\u90fd\u90fe\u90ff\u9100\u9101\u9102\u9103\u9104\u9105\u9106\u9107\u9108\u9109\u910a\u910b\u910c\u910d\u910e\u910f\u9110\u9111\u9112\u9113\u9114\u9115\u9116\u9117\u9118\u9119\u911a\u911b\u911c\u911d\u911e\u911f\u9120\u9121\u9122\u9123\u9124\u9125\u9126\u9127\u9128\u9129\u912a\u912b\u912c\u912d\u912e\u912f\u9130\u9131\u9132\u9133\u9134\u9135\u9136\u9137\u9138\u9139\u913a\u913b\u913c\u913d\u913e\u913f\u9140\u9141\u9142\u9143\u9144\u9145\u9146\u9147\u9148\u9149\u914a\u914b\u914c\u914d\u914e\u914f\u9150\u9151\u9152\u9153\u9154\u9155\u9156\u9157\u9158\u9159\u915a\u915b\u915c\u915d\u915e\u915f\u9160\u9161\u9162\u9163\u9164\u9165\u9166\u9167\u9168\u9169\u916a\u916b\u916c\u916d\u916e\u916f\u9170\u9171\u9172\u9173\u9174\u9175\u9176\u9177\u9178\u9179\u917a\u917b\u917c\u917d\u917e\u917f\u9180\u9181\u9182\u9183\u9184\u9185\u9186\u9187\u9188\u9189\u918a\u918b\u918c\u918d\u918e\u918f\u9190\u9191\u9192\u9193\u9194\u9195\u9196\u9197\u9198\u9199\u919a\u919b\u919c\u919d\u919e\u919f\u91a0\u91a1\u91a2\u91a3\u91a4\u91a5\u91a6\u91a7\u91a8\u91a9\u91aa\u91ab\u91ac\u91ad\u91ae\u91af\u91b0\u91b1\u91b2\u91b3\u91b4\u91b5\u91b6\u91b7\u91b8\u91b9\u91ba\u91bb\u91bc\u91bd\u91be\u91bf\u91c0\u91c1\u91c2\u91c3\u91c4\u91c5\u91c6\u91c7\u91c8\u91c9\u91ca\u91cb\u91cc\u91cd\u91ce\u91cf\u91d0\u91d1\u91d2\u91d3\u91d4\u91d5\u91d6\u91d7\u91d8\u91d9\u91da\u91db\u91dc\u91dd\u91de\u91df\u91e0\u91e1\u91e2\u91e3\u91e4\u91e5\u91e6\u91e7\u91e8\u91e9\u91ea\u91eb\u91ec\u91ed\u91ee\u91ef\u91f0\u91f1\u91f2\u91f3\u91f4\u91f5\u91f6\u91f7\u91f8\u91f9\u91fa\u91fb\u91fc\u91fd\u91fe\u91ff\u9200\u9201\u9202\u9203\u9204\u9205\u9206\u9207\u9208\u9209\u920a\u920b\u920c\u920d\u920e\u920f\u9210\u9211\u9212\u9213\u9214\u9215\u9216\u9217\u9218\u9219\u921a\u921b\u921c\u921d\u921e\u921f\u9220\u9221\u9222\u9223\u9224\u9225\u9226\u9227\u9228\u9229\u922a\u922b\u922c\u922d\u922e\u922f\u9230\u9231\u9232\u9233\u9234\u9235\u9236\u9237\u9238\u9239\u923a\u923b\u923c\u923d\u923e\u923f\u9240\u9241\u9242\u9243\u9244\u9245\u9246\u9247\u9248\u9249\u924a\u924b\u924c\u924d\u924e\u924f\u9250\u9251\u9252\u9253\u9254\u9255\u9256\u9257\u9258\u9259\u925a\u925b\u925c\u925d\u925e\u925f\u9260\u9261\u9262\u9263\u9264\u9265\u9266\u9267\u9268\u9269\u926a\u926b\u926c\u926d\u926e\u926f\u9270\u9271\u9272\u9273\u9274\u9275\u9276\u9277\u9278\u9279\u927a\u927b\u927c\u927d\u927e\u927f\u9280\u9281\u9282\u9283\u9284\u9285\u9286\u9287\u9288\u9289\u928a\u928b\u928c\u928d\u928e\u928f\u9290\u9291\u9292\u9293\u9294\u9295\u9296\u9297\u9298\u9299\u929a\u929b\u929c\u929d\u929e\u929f\u92a0\u92a1\u92a2\u92a3\u92a4\u92a5\u92a6\u92a7\u92a8\u92a9\u92aa\u92ab\u92ac\u92ad\u92ae\u92af\u92b0\u92b1\u92b2\u92b3\u92b4\u92b5\u92b6\u92b7\u92b8\u92b9\u92ba\u92bb\u92bc\u92bd\u92be\u92bf\u92c0\u92c1\u92c2\u92c3\u92c4\u92c5\u92c6\u92c7\u92c8\u92c9\u92ca\u92cb\u92cc\u92cd\u92ce\u92cf\u92d0\u92d1\u92d2\u92d3\u92d4\u92d5\u92d6\u92d7\u92d8\u92d9\u92da\u92db\u92dc\u92dd\u92de\u92df\u92e0\u92e1\u92e2\u92e3\u92e4\u92e5\u92e6\u92e7\u92e8\u92e9\u92ea\u92eb\u92ec\u92ed\u92ee\u92ef\u92f0\u92f1\u92f2\u92f3\u92f4\u92f5\u92f6\u92f7\u92f8\u92f9\u92fa\u92fb\u92fc\u92fd\u92fe\u92ff\u9300\u9301\u9302\u9303\u9304\u9305\u9306\u9307\u9308\u9309\u930a\u930b\u930c\u930d\u930e\u930f\u9310\u9311\u9312\u9313\u9314\u9315\u9316\u9317\u9318\u9319\u931a\u931b\u931c\u931d\u931e\u931f\u9320\u9321\u9322\u9323\u9324\u9325\u9326\u9327\u9328\u9329\u932a\u932b\u932c\u932d\u932e\u932f\u9330\u9331\u9332\u9333\u9334\u9335\u9336\u9337\u9338\u9339\u933a\u933b\u933c\u933d\u933e\u933f\u9340\u9341\u9342\u9343\u9344\u9345\u9346\u9347\u9348\u9349\u934a\u934b\u934c\u934d\u934e\u934f\u9350\u9351\u9352\u9353\u9354\u9355\u9356\u9357\u9358\u9359\u935a\u935b\u935c\u935d\u935e\u935f\u9360\u9361\u9362\u9363\u9364\u9365\u9366\u9367\u9368\u9369\u936a\u936b\u936c\u936d\u936e\u936f\u9370\u9371\u9372\u9373\u9374\u9375\u9376\u9377\u9378\u9379\u937a\u937b\u937c\u937d\u937e\u937f\u9380\u9381\u9382\u9383\u9384\u9385\u9386\u9387\u9388\u9389\u938a\u938b\u938c\u938d\u938e\u938f\u9390\u9391\u9392\u9393\u9394\u9395\u9396\u9397\u9398\u9399\u939a\u939b\u939c\u939d\u939e\u939f\u93a0\u93a1\u93a2\u93a3\u93a4\u93a5\u93a6\u93a7\u93a8\u93a9\u93aa\u93ab\u93ac\u93ad\u93ae\u93af\u93b0\u93b1\u93b2\u93b3\u93b4\u93b5\u93b6\u93b7\u93b8\u93b9\u93ba\u93bb\u93bc\u93bd\u93be\u93bf\u93c0\u93c1\u93c2\u93c3\u93c4\u93c5\u93c6\u93c7\u93c8\u93c9\u93ca\u93cb\u93cc\u93cd\u93ce\u93cf\u93d0\u93d1\u93d2\u93d3\u93d4\u93d5\u93d6\u93d7\u93d8\u93d9\u93da\u93db\u93dc\u93dd\u93de\u93df\u93e0\u93e1\u93e2\u93e3\u93e4\u93e5\u93e6\u93e7\u93e8\u93e9\u93ea\u93eb\u93ec\u93ed\u93ee\u93ef\u93f0\u93f1\u93f2\u93f3\u93f4\u93f5\u93f6\u93f7\u93f8\u93f9\u93fa\u93fb\u93fc\u93fd\u93fe\u93ff\u9400\u9401\u9402\u9403\u9404\u9405\u9406\u9407\u9408\u9409\u940a\u940b\u940c\u940d\u940e\u940f\u9410\u9411\u9412\u9413\u9414\u9415\u9416\u9417\u9418\u9419\u941a\u941b\u941c\u941d\u941e\u941f\u9420\u9421\u9422\u9423\u9424\u9425\u9426\u9427\u9428\u9429\u942a\u942b\u942c\u942d\u942e\u942f\u9430\u9431\u9432\u9433\u9434\u9435\u9436\u9437\u9438\u9439\u943a\u943b\u943c\u943d\u943e\u943f\u9440\u9441\u9442\u9443\u9444\u9445\u9446\u9447\u9448\u9449\u944a\u944b\u944c\u944d\u944e\u944f\u9450\u9451\u9452\u9453\u9454\u9455\u9456\u9457\u9458\u9459\u945a\u945b\u945c\u945d\u945e\u945f\u9460\u9461\u9462\u9463\u9464\u9465\u9466\u9467\u9468\u9469\u946a\u946b\u946c\u946d\u946e\u946f\u9470\u9471\u9472\u9473\u9474\u9475\u9476\u9477\u9478\u9479\u947a\u947b\u947c\u947d\u947e\u947f\u9480\u9481\u9482\u9483\u9484\u9485\u9486\u9487\u9488\u9489\u948a\u948b\u948c\u948d\u948e\u948f\u9490\u9491\u9492\u9493\u9494\u9495\u9496\u9497\u9498\u9499\u949a\u949b\u949c\u949d\u949e\u949f\u94a0\u94a1\u94a2\u94a3\u94a4\u94a5\u94a6\u94a7\u94a8\u94a9\u94aa\u94ab\u94ac\u94ad\u94ae\u94af\u94b0\u94b1\u94b2\u94b3\u94b4\u94b5\u94b6\u94b7\u94b8\u94b9\u94ba\u94bb\u94bc\u94bd\u94be\u94bf\u94c0\u94c1\u94c2\u94c3\u94c4\u94c5\u94c6\u94c7\u94c8\u94c9\u94ca\u94cb\u94cc\u94cd\u94ce\u94cf\u94d0\u94d1\u94d2\u94d3\u94d4\u94d5\u94d6\u94d7\u94d8\u94d9\u94da\u94db\u94dc\u94dd\u94de\u94df\u94e0\u94e1\u94e2\u94e3\u94e4\u94e5\u94e6\u94e7\u94e8\u94e9\u94ea\u94eb\u94ec\u94ed\u94ee\u94ef\u94f0\u94f1\u94f2\u94f3\u94f4\u94f5\u94f6\u94f7\u94f8\u94f9\u94fa\u94fb\u94fc\u94fd\u94fe\u94ff\u9500\u9501\u9502\u9503\u9504\u9505\u9506\u9507\u9508\u9509\u950a\u950b\u950c\u950d\u950e\u950f\u9510\u9511\u9512\u9513\u9514\u9515\u9516\u9517\u9518\u9519\u951a\u951b\u951c\u951d\u951e\u951f\u9520\u9521\u9522\u9523\u9524\u9525\u9526\u9527\u9528\u9529\u952a\u952b\u952c\u952d\u952e\u952f\u9530\u9531\u9532\u9533\u9534\u9535\u9536\u9537\u9538\u9539\u953a\u953b\u953c\u953d\u953e\u953f\u9540\u9541\u9542\u9543\u9544\u9545\u9546\u9547\u9548\u9549\u954a\u954b\u954c\u954d\u954e\u954f\u9550\u9551\u9552\u9553\u9554\u9555\u9556\u9557\u9558\u9559\u955a\u955b\u955c\u955d\u955e\u955f\u9560\u9561\u9562\u9563\u9564\u9565\u9566\u9567\u9568\u9569\u956a\u956b\u956c\u956d\u956e\u956f\u9570\u9571\u9572\u9573\u9574\u9575\u9576\u9577\u9578\u9579\u957a\u957b\u957c\u957d\u957e\u957f\u9580\u9581\u9582\u9583\u9584\u9585\u9586\u9587\u9588\u9589\u958a\u958b\u958c\u958d\u958e\u958f\u9590\u9591\u9592\u9593\u9594\u9595\u9596\u9597\u9598\u9599\u959a\u959b\u959c\u959d\u959e\u959f\u95a0\u95a1\u95a2\u95a3\u95a4\u95a5\u95a6\u95a7\u95a8\u95a9\u95aa\u95ab\u95ac\u95ad\u95ae\u95af\u95b0\u95b1\u95b2\u95b3\u95b4\u95b5\u95b6\u95b7\u95b8\u95b9\u95ba\u95bb\u95bc\u95bd\u95be\u95bf\u95c0\u95c1\u95c2\u95c3\u95c4\u95c5\u95c6\u95c7\u95c8\u95c9\u95ca\u95cb\u95cc\u95cd\u95ce\u95cf\u95d0\u95d1\u95d2\u95d3\u95d4\u95d5\u95d6\u95d7\u95d8\u95d9\u95da\u95db\u95dc\u95dd\u95de\u95df\u95e0\u95e1\u95e2\u95e3\u95e4\u95e5\u95e6\u95e7\u95e8\u95e9\u95ea\u95eb\u95ec\u95ed\u95ee\u95ef\u95f0\u95f1\u95f2\u95f3\u95f4\u95f5\u95f6\u95f7\u95f8\u95f9\u95fa\u95fb\u95fc\u95fd\u95fe\u95ff\u9600\u9601\u9602\u9603\u9604\u9605\u9606\u9607\u9608\u9609\u960a\u960b\u960c\u960d\u960e\u960f\u9610\u9611\u9612\u9613\u9614\u9615\u9616\u9617\u9618\u9619\u961a\u961b\u961c\u961d\u961e\u961f\u9620\u9621\u9622\u9623\u9624\u9625\u9626\u9627\u9628\u9629\u962a\u962b\u962c\u962d\u962e\u962f\u9630\u9631\u9632\u9633\u9634\u9635\u9636\u9637\u9638\u9639\u963a\u963b\u963c\u963d\u963e\u963f\u9640\u9641\u9642\u9643\u9644\u9645\u9646\u9647\u9648\u9649\u964a\u964b\u964c\u964d\u964e\u964f\u9650\u9651\u9652\u9653\u9654\u9655\u9656\u9657\u9658\u9659\u965a\u965b\u965c\u965d\u965e\u965f\u9660\u9661\u9662\u9663\u9664\u9665\u9666\u9667\u9668\u9669\u966a\u966b\u966c\u966d\u966e\u966f\u9670\u9671\u9672\u9673\u9674\u9675\u9676\u9677\u9678\u9679\u967a\u967b\u967c\u967d\u967e\u967f\u9680\u9681\u9682\u9683\u9684\u9685\u9686\u9687\u9688\u9689\u968a\u968b\u968c\u968d\u968e\u968f\u9690\u9691\u9692\u9693\u9694\u9695\u9696\u9697\u9698\u9699\u969a\u969b\u969c\u969d\u969e\u969f\u96a0\u96a1\u96a2\u96a3\u96a4\u96a5\u96a6\u96a7\u96a8\u96a9\u96aa\u96ab\u96ac\u96ad\u96ae\u96af\u96b0\u96b1\u96b2\u96b3\u96b4\u96b5\u96b6\u96b7\u96b8\u96b9\u96ba\u96bb\u96bc\u96bd\u96be\u96bf\u96c0\u96c1\u96c2\u96c3\u96c4\u96c5\u96c6\u96c7\u96c8\u96c9\u96ca\u96cb\u96cc\u96cd\u96ce\u96cf\u96d0\u96d1\u96d2\u96d3\u96d4\u96d5\u96d6\u96d7\u96d8\u96d9\u96da\u96db\u96dc\u96dd\u96de\u96df\u96e0\u96e1\u96e2\u96e3\u96e4\u96e5\u96e6\u96e7\u96e8\u96e9\u96ea\u96eb\u96ec\u96ed\u96ee\u96ef\u96f0\u96f1\u96f2\u96f3\u96f4\u96f5\u96f6\u96f7\u96f8\u96f9\u96fa\u96fb\u96fc\u96fd\u96fe\u96ff\u9700\u9701\u9702\u9703\u9704\u9705\u9706\u9707\u9708\u9709\u970a\u970b\u970c\u970d\u970e\u970f\u9710\u9711\u9712\u9713\u9714\u9715\u9716\u9717\u9718\u9719\u971a\u971b\u971c\u971d\u971e\u971f\u9720\u9721\u9722\u9723\u9724\u9725\u9726\u9727\u9728\u9729\u972a\u972b\u972c\u972d\u972e\u972f\u9730\u9731\u9732\u9733\u9734\u9735\u9736\u9737\u9738\u9739\u973a\u973b\u973c\u973d\u973e\u973f\u9740\u9741\u9742\u9743\u9744\u9745\u9746\u9747\u9748\u9749\u974a\u974b\u974c\u974d\u974e\u974f\u9750\u9751\u9752\u9753\u9754\u9755\u9756\u9757\u9758\u9759\u975a\u975b\u975c\u975d\u975e\u975f\u9760\u9761\u9762\u9763\u9764\u9765\u9766\u9767\u9768\u9769\u976a\u976b\u976c\u976d\u976e\u976f\u9770\u9771\u9772\u9773\u9774\u9775\u9776\u9777\u9778\u9779\u977a\u977b\u977c\u977d\u977e\u977f\u9780\u9781\u9782\u9783\u9784\u9785\u9786\u9787\u9788\u9789\u978a\u978b\u978c\u978d\u978e\u978f\u9790\u9791\u9792\u9793\u9794\u9795\u9796\u9797\u9798\u9799\u979a\u979b\u979c\u979d\u979e\u979f\u97a0\u97a1\u97a2\u97a3\u97a4\u97a5\u97a6\u97a7\u97a8\u97a9\u97aa\u97ab\u97ac\u97ad\u97ae\u97af\u97b0\u97b1\u97b2\u97b3\u97b4\u97b5\u97b6\u97b7\u97b8\u97b9\u97ba\u97bb\u97bc\u97bd\u97be\u97bf\u97c0\u97c1\u97c2\u97c3\u97c4\u97c5\u97c6\u97c7\u97c8\u97c9\u97ca\u97cb\u97cc\u97cd\u97ce\u97cf\u97d0\u97d1\u97d2\u97d3\u97d4\u97d5\u97d6\u97d7\u97d8\u97d9\u97da\u97db\u97dc\u97dd\u97de\u97df\u97e0\u97e1\u97e2\u97e3\u97e4\u97e5\u97e6\u97e7\u97e8\u97e9\u97ea\u97eb\u97ec\u97ed\u97ee\u97ef\u97f0\u97f1\u97f2\u97f3\u97f4\u97f5\u97f6\u97f7\u97f8\u97f9\u97fa\u97fb\u97fc\u97fd\u97fe\u97ff\u9800\u9801\u9802\u9803\u9804\u9805\u9806\u9807\u9808\u9809\u980a\u980b\u980c\u980d\u980e\u980f\u9810\u9811\u9812\u9813\u9814\u9815\u9816\u9817\u9818\u9819\u981a\u981b\u981c\u981d\u981e\u981f\u9820\u9821\u9822\u9823\u9824\u9825\u9826\u9827\u9828\u9829\u982a\u982b\u982c\u982d\u982e\u982f\u9830\u9831\u9832\u9833\u9834\u9835\u9836\u9837\u9838\u9839\u983a\u983b\u983c\u983d\u983e\u983f\u9840\u9841\u9842\u9843\u9844\u9845\u9846\u9847\u9848\u9849\u984a\u984b\u984c\u984d\u984e\u984f\u9850\u9851\u9852\u9853\u9854\u9855\u9856\u9857\u9858\u9859\u985a\u985b\u985c\u985d\u985e\u985f\u9860\u9861\u9862\u9863\u9864\u9865\u9866\u9867\u9868\u9869\u986a\u986b\u986c\u986d\u986e\u986f\u9870\u9871\u9872\u9873\u9874\u9875\u9876\u9877\u9878\u9879\u987a\u987b\u987c\u987d\u987e\u987f\u9880\u9881\u9882\u9883\u9884\u9885\u9886\u9887\u9888\u9889\u988a\u988b\u988c\u988d\u988e\u988f\u9890\u9891\u9892\u9893\u9894\u9895\u9896\u9897\u9898\u9899\u989a\u989b\u989c\u989d\u989e\u989f\u98a0\u98a1\u98a2\u98a3\u98a4\u98a5\u98a6\u98a7\u98a8\u98a9\u98aa\u98ab\u98ac\u98ad\u98ae\u98af\u98b0\u98b1\u98b2\u98b3\u98b4\u98b5\u98b6\u98b7\u98b8\u98b9\u98ba\u98bb\u98bc\u98bd\u98be\u98bf\u98c0\u98c1\u98c2\u98c3\u98c4\u98c5\u98c6\u98c7\u98c8\u98c9\u98ca\u98cb\u98cc\u98cd\u98ce\u98cf\u98d0\u98d1\u98d2\u98d3\u98d4\u98d5\u98d6\u98d7\u98d8\u98d9\u98da\u98db\u98dc\u98dd\u98de\u98df\u98e0\u98e1\u98e2\u98e3\u98e4\u98e5\u98e6\u98e7\u98e8\u98e9\u98ea\u98eb\u98ec\u98ed\u98ee\u98ef\u98f0\u98f1\u98f2\u98f3\u98f4\u98f5\u98f6\u98f7\u98f8\u98f9\u98fa\u98fb\u98fc\u98fd\u98fe\u98ff\u9900\u9901\u9902\u9903\u9904\u9905\u9906\u9907\u9908\u9909\u990a\u990b\u990c\u990d\u990e\u990f\u9910\u9911\u9912\u9913\u9914\u9915\u9916\u9917\u9918\u9919\u991a\u991b\u991c\u991d\u991e\u991f\u9920\u9921\u9922\u9923\u9924\u9925\u9926\u9927\u9928\u9929\u992a\u992b\u992c\u992d\u992e\u992f\u9930\u9931\u9932\u9933\u9934\u9935\u9936\u9937\u9938\u9939\u993a\u993b\u993c\u993d\u993e\u993f\u9940\u9941\u9942\u9943\u9944\u9945\u9946\u9947\u9948\u9949\u994a\u994b\u994c\u994d\u994e\u994f\u9950\u9951\u9952\u9953\u9954\u9955\u9956\u9957\u9958\u9959\u995a\u995b\u995c\u995d\u995e\u995f\u9960\u9961\u9962\u9963\u9964\u9965\u9966\u9967\u9968\u9969\u996a\u996b\u996c\u996d\u996e\u996f\u9970\u9971\u9972\u9973\u9974\u9975\u9976\u9977\u9978\u9979\u997a\u997b\u997c\u997d\u997e\u997f\u9980\u9981\u9982\u9983\u9984\u9985\u9986\u9987\u9988\u9989\u998a\u998b\u998c\u998d\u998e\u998f\u9990\u9991\u9992\u9993\u9994\u9995\u9996\u9997\u9998\u9999\u999a\u999b\u999c\u999d\u999e\u999f\u99a0\u99a1\u99a2\u99a3\u99a4\u99a5\u99a6\u99a7\u99a8\u99a9\u99aa\u99ab\u99ac\u99ad\u99ae\u99af\u99b0\u99b1\u99b2\u99b3\u99b4\u99b5\u99b6\u99b7\u99b8\u99b9\u99ba\u99bb\u99bc\u99bd\u99be\u99bf\u99c0\u99c1\u99c2\u99c3\u99c4\u99c5\u99c6\u99c7\u99c8\u99c9\u99ca\u99cb\u99cc\u99cd\u99ce\u99cf\u99d0\u99d1\u99d2\u99d3\u99d4\u99d5\u99d6\u99d7\u99d8\u99d9\u99da\u99db\u99dc\u99dd\u99de\u99df\u99e0\u99e1\u99e2\u99e3\u99e4\u99e5\u99e6\u99e7\u99e8\u99e9\u99ea\u99eb\u99ec\u99ed\u99ee\u99ef\u99f0\u99f1\u99f2\u99f3\u99f4\u99f5\u99f6\u99f7\u99f8\u99f9\u99fa\u99fb\u99fc\u99fd\u99fe\u99ff\u9a00\u9a01\u9a02\u9a03\u9a04\u9a05\u9a06\u9a07\u9a08\u9a09\u9a0a\u9a0b\u9a0c\u9a0d\u9a0e\u9a0f\u9a10\u9a11\u9a12\u9a13\u9a14\u9a15\u9a16\u9a17\u9a18\u9a19\u9a1a\u9a1b\u9a1c\u9a1d\u9a1e\u9a1f\u9a20\u9a21\u9a22\u9a23\u9a24\u9a25\u9a26\u9a27\u9a28\u9a29\u9a2a\u9a2b\u9a2c\u9a2d\u9a2e\u9a2f\u9a30\u9a31\u9a32\u9a33\u9a34\u9a35\u9a36\u9a37\u9a38\u9a39\u9a3a\u9a3b\u9a3c\u9a3d\u9a3e\u9a3f\u9a40\u9a41\u9a42\u9a43\u9a44\u9a45\u9a46\u9a47\u9a48\u9a49\u9a4a\u9a4b\u9a4c\u9a4d\u9a4e\u9a4f\u9a50\u9a51\u9a52\u9a53\u9a54\u9a55\u9a56\u9a57\u9a58\u9a59\u9a5a\u9a5b\u9a5c\u9a5d\u9a5e\u9a5f\u9a60\u9a61\u9a62\u9a63\u9a64\u9a65\u9a66\u9a67\u9a68\u9a69\u9a6a\u9a6b\u9a6c\u9a6d\u9a6e\u9a6f\u9a70\u9a71\u9a72\u9a73\u9a74\u9a75\u9a76\u9a77\u9a78\u9a79\u9a7a\u9a7b\u9a7c\u9a7d\u9a7e\u9a7f\u9a80\u9a81\u9a82\u9a83\u9a84\u9a85\u9a86\u9a87\u9a88\u9a89\u9a8a\u9a8b\u9a8c\u9a8d\u9a8e\u9a8f\u9a90\u9a91\u9a92\u9a93\u9a94\u9a95\u9a96\u9a97\u9a98\u9a99\u9a9a\u9a9b\u9a9c\u9a9d\u9a9e\u9a9f\u9aa0\u9aa1\u9aa2\u9aa3\u9aa4\u9aa5\u9aa6\u9aa7\u9aa8\u9aa9\u9aaa\u9aab\u9aac\u9aad\u9aae\u9aaf\u9ab0\u9ab1\u9ab2\u9ab3\u9ab4\u9ab5\u9ab6\u9ab7\u9ab8\u9ab9\u9aba\u9abb\u9abc\u9abd\u9abe\u9abf\u9ac0\u9ac1\u9ac2\u9ac3\u9ac4\u9ac5\u9ac6\u9ac7\u9ac8\u9ac9\u9aca\u9acb\u9acc\u9acd\u9ace\u9acf\u9ad0\u9ad1\u9ad2\u9ad3\u9ad4\u9ad5\u9ad6\u9ad7\u9ad8\u9ad9\u9ada\u9adb\u9adc\u9add\u9ade\u9adf\u9ae0\u9ae1\u9ae2\u9ae3\u9ae4\u9ae5\u9ae6\u9ae7\u9ae8\u9ae9\u9aea\u9aeb\u9aec\u9aed\u9aee\u9aef\u9af0\u9af1\u9af2\u9af3\u9af4\u9af5\u9af6\u9af7\u9af8\u9af9\u9afa\u9afb\u9afc\u9afd\u9afe\u9aff\u9b00\u9b01\u9b02\u9b03\u9b04\u9b05\u9b06\u9b07\u9b08\u9b09\u9b0a\u9b0b\u9b0c\u9b0d\u9b0e\u9b0f\u9b10\u9b11\u9b12\u9b13\u9b14\u9b15\u9b16\u9b17\u9b18\u9b19\u9b1a\u9b1b\u9b1c\u9b1d\u9b1e\u9b1f\u9b20\u9b21\u9b22\u9b23\u9b24\u9b25\u9b26\u9b27\u9b28\u9b29\u9b2a\u9b2b\u9b2c\u9b2d\u9b2e\u9b2f\u9b30\u9b31\u9b32\u9b33\u9b34\u9b35\u9b36\u9b37\u9b38\u9b39\u9b3a\u9b3b\u9b3c\u9b3d\u9b3e\u9b3f\u9b40\u9b41\u9b42\u9b43\u9b44\u9b45\u9b46\u9b47\u9b48\u9b49\u9b4a\u9b4b\u9b4c\u9b4d\u9b4e\u9b4f\u9b50\u9b51\u9b52\u9b53\u9b54\u9b55\u9b56\u9b57\u9b58\u9b59\u9b5a\u9b5b\u9b5c\u9b5d\u9b5e\u9b5f\u9b60\u9b61\u9b62\u9b63\u9b64\u9b65\u9b66\u9b67\u9b68\u9b69\u9b6a\u9b6b\u9b6c\u9b6d\u9b6e\u9b6f\u9b70\u9b71\u9b72\u9b73\u9b74\u9b75\u9b76\u9b77\u9b78\u9b79\u9b7a\u9b7b\u9b7c\u9b7d\u9b7e\u9b7f\u9b80\u9b81\u9b82\u9b83\u9b84\u9b85\u9b86\u9b87\u9b88\u9b89\u9b8a\u9b8b\u9b8c\u9b8d\u9b8e\u9b8f\u9b90\u9b91\u9b92\u9b93\u9b94\u9b95\u9b96\u9b97\u9b98\u9b99\u9b9a\u9b9b\u9b9c\u9b9d\u9b9e\u9b9f\u9ba0\u9ba1\u9ba2\u9ba3\u9ba4\u9ba5\u9ba6\u9ba7\u9ba8\u9ba9\u9baa\u9bab\u9bac\u9bad\u9bae\u9baf\u9bb0\u9bb1\u9bb2\u9bb3\u9bb4\u9bb5\u9bb6\u9bb7\u9bb8\u9bb9\u9bba\u9bbb\u9bbc\u9bbd\u9bbe\u9bbf\u9bc0\u9bc1\u9bc2\u9bc3\u9bc4\u9bc5\u9bc6\u9bc7\u9bc8\u9bc9\u9bca\u9bcb\u9bcc\u9bcd\u9bce\u9bcf\u9bd0\u9bd1\u9bd2\u9bd3\u9bd4\u9bd5\u9bd6\u9bd7\u9bd8\u9bd9\u9bda\u9bdb\u9bdc\u9bdd\u9bde\u9bdf\u9be0\u9be1\u9be2\u9be3\u9be4\u9be5\u9be6\u9be7\u9be8\u9be9\u9bea\u9beb\u9bec\u9bed\u9bee\u9bef\u9bf0\u9bf1\u9bf2\u9bf3\u9bf4\u9bf5\u9bf6\u9bf7\u9bf8\u9bf9\u9bfa\u9bfb\u9bfc\u9bfd\u9bfe\u9bff\u9c00\u9c01\u9c02\u9c03\u9c04\u9c05\u9c06\u9c07\u9c08\u9c09\u9c0a\u9c0b\u9c0c\u9c0d\u9c0e\u9c0f\u9c10\u9c11\u9c12\u9c13\u9c14\u9c15\u9c16\u9c17\u9c18\u9c19\u9c1a\u9c1b\u9c1c\u9c1d\u9c1e\u9c1f\u9c20\u9c21\u9c22\u9c23\u9c24\u9c25\u9c26\u9c27\u9c28\u9c29\u9c2a\u9c2b\u9c2c\u9c2d\u9c2e\u9c2f\u9c30\u9c31\u9c32\u9c33\u9c34\u9c35\u9c36\u9c37\u9c38\u9c39\u9c3a\u9c3b\u9c3c\u9c3d\u9c3e\u9c3f\u9c40\u9c41\u9c42\u9c43\u9c44\u9c45\u9c46\u9c47\u9c48\u9c49\u9c4a\u9c4b\u9c4c\u9c4d\u9c4e\u9c4f\u9c50\u9c51\u9c52\u9c53\u9c54\u9c55\u9c56\u9c57\u9c58\u9c59\u9c5a\u9c5b\u9c5c\u9c5d\u9c5e\u9c5f\u9c60\u9c61\u9c62\u9c63\u9c64\u9c65\u9c66\u9c67\u9c68\u9c69\u9c6a\u9c6b\u9c6c\u9c6d\u9c6e\u9c6f\u9c70\u9c71\u9c72\u9c73\u9c74\u9c75\u9c76\u9c77\u9c78\u9c79\u9c7a\u9c7b\u9c7c\u9c7d\u9c7e\u9c7f\u9c80\u9c81\u9c82\u9c83\u9c84\u9c85\u9c86\u9c87\u9c88\u9c89\u9c8a\u9c8b\u9c8c\u9c8d\u9c8e\u9c8f\u9c90\u9c91\u9c92\u9c93\u9c94\u9c95\u9c96\u9c97\u9c98\u9c99\u9c9a\u9c9b\u9c9c\u9c9d\u9c9e\u9c9f\u9ca0\u9ca1\u9ca2\u9ca3\u9ca4\u9ca5\u9ca6\u9ca7\u9ca8\u9ca9\u9caa\u9cab\u9cac\u9cad\u9cae\u9caf\u9cb0\u9cb1\u9cb2\u9cb3\u9cb4\u9cb5\u9cb6\u9cb7\u9cb8\u9cb9\u9cba\u9cbb\u9cbc\u9cbd\u9cbe\u9cbf\u9cc0\u9cc1\u9cc2\u9cc3\u9cc4\u9cc5\u9cc6\u9cc7\u9cc8\u9cc9\u9cca\u9ccb\u9ccc\u9ccd\u9cce\u9ccf\u9cd0\u9cd1\u9cd2\u9cd3\u9cd4\u9cd5\u9cd6\u9cd7\u9cd8\u9cd9\u9cda\u9cdb\u9cdc\u9cdd\u9cde\u9cdf\u9ce0\u9ce1\u9ce2\u9ce3\u9ce4\u9ce5\u9ce6\u9ce7\u9ce8\u9ce9\u9cea\u9ceb\u9cec\u9ced\u9cee\u9cef\u9cf0\u9cf1\u9cf2\u9cf3\u9cf4\u9cf5\u9cf6\u9cf7\u9cf8\u9cf9\u9cfa\u9cfb\u9cfc\u9cfd\u9cfe\u9cff\u9d00\u9d01\u9d02\u9d03\u9d04\u9d05\u9d06\u9d07\u9d08\u9d09\u9d0a\u9d0b\u9d0c\u9d0d\u9d0e\u9d0f\u9d10\u9d11\u9d12\u9d13\u9d14\u9d15\u9d16\u9d17\u9d18\u9d19\u9d1a\u9d1b\u9d1c\u9d1d\u9d1e\u9d1f\u9d20\u9d21\u9d22\u9d23\u9d24\u9d25\u9d26\u9d27\u9d28\u9d29\u9d2a\u9d2b\u9d2c\u9d2d\u9d2e\u9d2f\u9d30\u9d31\u9d32\u9d33\u9d34\u9d35\u9d36\u9d37\u9d38\u9d39\u9d3a\u9d3b\u9d3c\u9d3d\u9d3e\u9d3f\u9d40\u9d41\u9d42\u9d43\u9d44\u9d45\u9d46\u9d47\u9d48\u9d49\u9d4a\u9d4b\u9d4c\u9d4d\u9d4e\u9d4f\u9d50\u9d51\u9d52\u9d53\u9d54\u9d55\u9d56\u9d57\u9d58\u9d59\u9d5a\u9d5b\u9d5c\u9d5d\u9d5e\u9d5f\u9d60\u9d61\u9d62\u9d63\u9d64\u9d65\u9d66\u9d67\u9d68\u9d69\u9d6a\u9d6b\u9d6c\u9d6d\u9d6e\u9d6f\u9d70\u9d71\u9d72\u9d73\u9d74\u9d75\u9d76\u9d77\u9d78\u9d79\u9d7a\u9d7b\u9d7c\u9d7d\u9d7e\u9d7f\u9d80\u9d81\u9d82\u9d83\u9d84\u9d85\u9d86\u9d87\u9d88\u9d89\u9d8a\u9d8b\u9d8c\u9d8d\u9d8e\u9d8f\u9d90\u9d91\u9d92\u9d93\u9d94\u9d95\u9d96\u9d97\u9d98\u9d99\u9d9a\u9d9b\u9d9c\u9d9d\u9d9e\u9d9f\u9da0\u9da1\u9da2\u9da3\u9da4\u9da5\u9da6\u9da7\u9da8\u9da9\u9daa\u9dab\u9dac\u9dad\u9dae\u9daf\u9db0\u9db1\u9db2\u9db3\u9db4\u9db5\u9db6\u9db7\u9db8\u9db9\u9dba\u9dbb\u9dbc\u9dbd\u9dbe\u9dbf\u9dc0\u9dc1\u9dc2\u9dc3\u9dc4\u9dc5\u9dc6\u9dc7\u9dc8\u9dc9\u9dca\u9dcb\u9dcc\u9dcd\u9dce\u9dcf\u9dd0\u9dd1\u9dd2\u9dd3\u9dd4\u9dd5\u9dd6\u9dd7\u9dd8\u9dd9\u9dda\u9ddb\u9ddc\u9ddd\u9dde\u9ddf\u9de0\u9de1\u9de2\u9de3\u9de4\u9de5\u9de6\u9de7\u9de8\u9de9\u9dea\u9deb\u9dec\u9ded\u9dee\u9def\u9df0\u9df1\u9df2\u9df3\u9df4\u9df5\u9df6\u9df7\u9df8\u9df9\u9dfa\u9dfb\u9dfc\u9dfd\u9dfe\u9dff\u9e00\u9e01\u9e02\u9e03\u9e04\u9e05\u9e06\u9e07\u9e08\u9e09\u9e0a\u9e0b\u9e0c\u9e0d\u9e0e\u9e0f\u9e10\u9e11\u9e12\u9e13\u9e14\u9e15\u9e16\u9e17\u9e18\u9e19\u9e1a\u9e1b\u9e1c\u9e1d\u9e1e\u9e1f\u9e20\u9e21\u9e22\u9e23\u9e24\u9e25\u9e26\u9e27\u9e28\u9e29\u9e2a\u9e2b\u9e2c\u9e2d\u9e2e\u9e2f\u9e30\u9e31\u9e32\u9e33\u9e34\u9e35\u9e36\u9e37\u9e38\u9e39\u9e3a\u9e3b\u9e3c\u9e3d\u9e3e\u9e3f\u9e40\u9e41\u9e42\u9e43\u9e44\u9e45\u9e46\u9e47\u9e48\u9e49\u9e4a\u9e4b\u9e4c\u9e4d\u9e4e\u9e4f\u9e50\u9e51\u9e52\u9e53\u9e54\u9e55\u9e56\u9e57\u9e58\u9e59\u9e5a\u9e5b\u9e5c\u9e5d\u9e5e\u9e5f\u9e60\u9e61\u9e62\u9e63\u9e64\u9e65\u9e66\u9e67\u9e68\u9e69\u9e6a\u9e6b\u9e6c\u9e6d\u9e6e\u9e6f\u9e70\u9e71\u9e72\u9e73\u9e74\u9e75\u9e76\u9e77\u9e78\u9e79\u9e7a\u9e7b\u9e7c\u9e7d\u9e7e\u9e7f\u9e80\u9e81\u9e82\u9e83\u9e84\u9e85\u9e86\u9e87\u9e88\u9e89\u9e8a\u9e8b\u9e8c\u9e8d\u9e8e\u9e8f\u9e90\u9e91\u9e92\u9e93\u9e94\u9e95\u9e96\u9e97\u9e98\u9e99\u9e9a\u9e9b\u9e9c\u9e9d\u9e9e\u9e9f\u9ea0\u9ea1\u9ea2\u9ea3\u9ea4\u9ea5\u9ea6\u9ea7\u9ea8\u9ea9\u9eaa\u9eab\u9eac\u9ead\u9eae\u9eaf\u9eb0\u9eb1\u9eb2\u9eb3\u9eb4\u9eb5\u9eb6\u9eb7\u9eb8\u9eb9\u9eba\u9ebb\u9ebc\u9ebd\u9ebe\u9ebf\u9ec0\u9ec1\u9ec2\u9ec3\u9ec4\u9ec5\u9ec6\u9ec7\u9ec8\u9ec9\u9eca\u9ecb\u9ecc\u9ecd\u9ece\u9ecf\u9ed0\u9ed1\u9ed2\u9ed3\u9ed4\u9ed5\u9ed6\u9ed7\u9ed8\u9ed9\u9eda\u9edb\u9edc\u9edd\u9ede\u9edf\u9ee0\u9ee1\u9ee2\u9ee3\u9ee4\u9ee5\u9ee6\u9ee7\u9ee8\u9ee9\u9eea\u9eeb\u9eec\u9eed\u9eee\u9eef\u9ef0\u9ef1\u9ef2\u9ef3\u9ef4\u9ef5\u9ef6\u9ef7\u9ef8\u9ef9\u9efa\u9efb\u9efc\u9efd\u9efe\u9eff\u9f00\u9f01\u9f02\u9f03\u9f04\u9f05\u9f06\u9f07\u9f08\u9f09\u9f0a\u9f0b\u9f0c\u9f0d\u9f0e\u9f0f\u9f10\u9f11\u9f12\u9f13\u9f14\u9f15\u9f16\u9f17\u9f18\u9f19\u9f1a\u9f1b\u9f1c\u9f1d\u9f1e\u9f1f\u9f20\u9f21\u9f22\u9f23\u9f24\u9f25\u9f26\u9f27\u9f28\u9f29\u9f2a\u9f2b\u9f2c\u9f2d\u9f2e\u9f2f\u9f30\u9f31\u9f32\u9f33\u9f34\u9f35\u9f36\u9f37\u9f38\u9f39\u9f3a\u9f3b\u9f3c\u9f3d\u9f3e\u9f3f\u9f40\u9f41\u9f42\u9f43\u9f44\u9f45\u9f46\u9f47\u9f48\u9f49\u9f4a\u9f4b\u9f4c\u9f4d\u9f4e\u9f4f\u9f50\u9f51\u9f52\u9f53\u9f54\u9f55\u9f56\u9f57\u9f58\u9f59\u9f5a\u9f5b\u9f5c\u9f5d\u9f5e\u9f5f\u9f60\u9f61\u9f62\u9f63\u9f64\u9f65\u9f66\u9f67\u9f68\u9f69\u9f6a\u9f6b\u9f6c\u9f6d\u9f6e\u9f6f\u9f70\u9f71\u9f72\u9f73\u9f74\u9f75\u9f76\u9f77\u9f78\u9f79\u9f7a\u9f7b\u9f7c\u9f7d\u9f7e\u9f7f\u9f80\u9f81\u9f82\u9f83\u9f84\u9f85\u9f86\u9f87\u9f88\u9f89\u9f8a\u9f8b\u9f8c\u9f8d\u9f8e\u9f8f\u9f90\u9f91\u9f92\u9f93\u9f94\u9f95\u9f96\u9f97\u9f98\u9f99\u9f9a\u9f9b\u9f9c\u9f9d\u9f9e\u9f9f\u9fa0\u9fa1\u9fa2\u9fa3\u9fa4\u9fa5\u9fa6\u9fa7\u9fa8\u9fa9\u9faa\u9fab\u9fac\u9fad\u9fae\u9faf\u9fb0\u9fb1\u9fb2\u9fb3\u9fb4\u9fb5\u9fb6\u9fb7\u9fb8\u9fb9\u9fba\u9fbb\ua000\ua001\ua002\ua003\ua004\ua005\ua006\ua007\ua008\ua009\ua00a\ua00b\ua00c\ua00d\ua00e\ua00f\ua010\ua011\ua012\ua013\ua014\ua016\ua017\ua018\ua019\ua01a\ua01b\ua01c\ua01d\ua01e\ua01f\ua020\ua021\ua022\ua023\ua024\ua025\ua026\ua027\ua028\ua029\ua02a\ua02b\ua02c\ua02d\ua02e\ua02f\ua030\ua031\ua032\ua033\ua034\ua035\ua036\ua037\ua038\ua039\ua03a\ua03b\ua03c\ua03d\ua03e\ua03f\ua040\ua041\ua042\ua043\ua044\ua045\ua046\ua047\ua048\ua049\ua04a\ua04b\ua04c\ua04d\ua04e\ua04f\ua050\ua051\ua052\ua053\ua054\ua055\ua056\ua057\ua058\ua059\ua05a\ua05b\ua05c\ua05d\ua05e\ua05f\ua060\ua061\ua062\ua063\ua064\ua065\ua066\ua067\ua068\ua069\ua06a\ua06b\ua06c\ua06d\ua06e\ua06f\ua070\ua071\ua072\ua073\ua074\ua075\ua076\ua077\ua078\ua079\ua07a\ua07b\ua07c\ua07d\ua07e\ua07f\ua080\ua081\ua082\ua083\ua084\ua085\ua086\ua087\ua088\ua089\ua08a\ua08b\ua08c\ua08d\ua08e\ua08f\ua090\ua091\ua092\ua093\ua094\ua095\ua096\ua097\ua098\ua099\ua09a\ua09b\ua09c\ua09d\ua09e\ua09f\ua0a0\ua0a1\ua0a2\ua0a3\ua0a4\ua0a5\ua0a6\ua0a7\ua0a8\ua0a9\ua0aa\ua0ab\ua0ac\ua0ad\ua0ae\ua0af\ua0b0\ua0b1\ua0b2\ua0b3\ua0b4\ua0b5\ua0b6\ua0b7\ua0b8\ua0b9\ua0ba\ua0bb\ua0bc\ua0bd\ua0be\ua0bf\ua0c0\ua0c1\ua0c2\ua0c3\ua0c4\ua0c5\ua0c6\ua0c7\ua0c8\ua0c9\ua0ca\ua0cb\ua0cc\ua0cd\ua0ce\ua0cf\ua0d0\ua0d1\ua0d2\ua0d3\ua0d4\ua0d5\ua0d6\ua0d7\ua0d8\ua0d9\ua0da\ua0db\ua0dc\ua0dd\ua0de\ua0df\ua0e0\ua0e1\ua0e2\ua0e3\ua0e4\ua0e5\ua0e6\ua0e7\ua0e8\ua0e9\ua0ea\ua0eb\ua0ec\ua0ed\ua0ee\ua0ef\ua0f0\ua0f1\ua0f2\ua0f3\ua0f4\ua0f5\ua0f6\ua0f7\ua0f8\ua0f9\ua0fa\ua0fb\ua0fc\ua0fd\ua0fe\ua0ff\ua100\ua101\ua102\ua103\ua104\ua105\ua106\ua107\ua108\ua109\ua10a\ua10b\ua10c\ua10d\ua10e\ua10f\ua110\ua111\ua112\ua113\ua114\ua115\ua116\ua117\ua118\ua119\ua11a\ua11b\ua11c\ua11d\ua11e\ua11f\ua120\ua121\ua122\ua123\ua124\ua125\ua126\ua127\ua128\ua129\ua12a\ua12b\ua12c\ua12d\ua12e\ua12f\ua130\ua131\ua132\ua133\ua134\ua135\ua136\ua137\ua138\ua139\ua13a\ua13b\ua13c\ua13d\ua13e\ua13f\ua140\ua141\ua142\ua143\ua144\ua145\ua146\ua147\ua148\ua149\ua14a\ua14b\ua14c\ua14d\ua14e\ua14f\ua150\ua151\ua152\ua153\ua154\ua155\ua156\ua157\ua158\ua159\ua15a\ua15b\ua15c\ua15d\ua15e\ua15f\ua160\ua161\ua162\ua163\ua164\ua165\ua166\ua167\ua168\ua169\ua16a\ua16b\ua16c\ua16d\ua16e\ua16f\ua170\ua171\ua172\ua173\ua174\ua175\ua176\ua177\ua178\ua179\ua17a\ua17b\ua17c\ua17d\ua17e\ua17f\ua180\ua181\ua182\ua183\ua184\ua185\ua186\ua187\ua188\ua189\ua18a\ua18b\ua18c\ua18d\ua18e\ua18f\ua190\ua191\ua192\ua193\ua194\ua195\ua196\ua197\ua198\ua199\ua19a\ua19b\ua19c\ua19d\ua19e\ua19f\ua1a0\ua1a1\ua1a2\ua1a3\ua1a4\ua1a5\ua1a6\ua1a7\ua1a8\ua1a9\ua1aa\ua1ab\ua1ac\ua1ad\ua1ae\ua1af\ua1b0\ua1b1\ua1b2\ua1b3\ua1b4\ua1b5\ua1b6\ua1b7\ua1b8\ua1b9\ua1ba\ua1bb\ua1bc\ua1bd\ua1be\ua1bf\ua1c0\ua1c1\ua1c2\ua1c3\ua1c4\ua1c5\ua1c6\ua1c7\ua1c8\ua1c9\ua1ca\ua1cb\ua1cc\ua1cd\ua1ce\ua1cf\ua1d0\ua1d1\ua1d2\ua1d3\ua1d4\ua1d5\ua1d6\ua1d7\ua1d8\ua1d9\ua1da\ua1db\ua1dc\ua1dd\ua1de\ua1df\ua1e0\ua1e1\ua1e2\ua1e3\ua1e4\ua1e5\ua1e6\ua1e7\ua1e8\ua1e9\ua1ea\ua1eb\ua1ec\ua1ed\ua1ee\ua1ef\ua1f0\ua1f1\ua1f2\ua1f3\ua1f4\ua1f5\ua1f6\ua1f7\ua1f8\ua1f9\ua1fa\ua1fb\ua1fc\ua1fd\ua1fe\ua1ff\ua200\ua201\ua202\ua203\ua204\ua205\ua206\ua207\ua208\ua209\ua20a\ua20b\ua20c\ua20d\ua20e\ua20f\ua210\ua211\ua212\ua213\ua214\ua215\ua216\ua217\ua218\ua219\ua21a\ua21b\ua21c\ua21d\ua21e\ua21f\ua220\ua221\ua222\ua223\ua224\ua225\ua226\ua227\ua228\ua229\ua22a\ua22b\ua22c\ua22d\ua22e\ua22f\ua230\ua231\ua232\ua233\ua234\ua235\ua236\ua237\ua238\ua239\ua23a\ua23b\ua23c\ua23d\ua23e\ua23f\ua240\ua241\ua242\ua243\ua244\ua245\ua246\ua247\ua248\ua249\ua24a\ua24b\ua24c\ua24d\ua24e\ua24f\ua250\ua251\ua252\ua253\ua254\ua255\ua256\ua257\ua258\ua259\ua25a\ua25b\ua25c\ua25d\ua25e\ua25f\ua260\ua261\ua262\ua263\ua264\ua265\ua266\ua267\ua268\ua269\ua26a\ua26b\ua26c\ua26d\ua26e\ua26f\ua270\ua271\ua272\ua273\ua274\ua275\ua276\ua277\ua278\ua279\ua27a\ua27b\ua27c\ua27d\ua27e\ua27f\ua280\ua281\ua282\ua283\ua284\ua285\ua286\ua287\ua288\ua289\ua28a\ua28b\ua28c\ua28d\ua28e\ua28f\ua290\ua291\ua292\ua293\ua294\ua295\ua296\ua297\ua298\ua299\ua29a\ua29b\ua29c\ua29d\ua29e\ua29f\ua2a0\ua2a1\ua2a2\ua2a3\ua2a4\ua2a5\ua2a6\ua2a7\ua2a8\ua2a9\ua2aa\ua2ab\ua2ac\ua2ad\ua2ae\ua2af\ua2b0\ua2b1\ua2b2\ua2b3\ua2b4\ua2b5\ua2b6\ua2b7\ua2b8\ua2b9\ua2ba\ua2bb\ua2bc\ua2bd\ua2be\ua2bf\ua2c0\ua2c1\ua2c2\ua2c3\ua2c4\ua2c5\ua2c6\ua2c7\ua2c8\ua2c9\ua2ca\ua2cb\ua2cc\ua2cd\ua2ce\ua2cf\ua2d0\ua2d1\ua2d2\ua2d3\ua2d4\ua2d5\ua2d6\ua2d7\ua2d8\ua2d9\ua2da\ua2db\ua2dc\ua2dd\ua2de\ua2df\ua2e0\ua2e1\ua2e2\ua2e3\ua2e4\ua2e5\ua2e6\ua2e7\ua2e8\ua2e9\ua2ea\ua2eb\ua2ec\ua2ed\ua2ee\ua2ef\ua2f0\ua2f1\ua2f2\ua2f3\ua2f4\ua2f5\ua2f6\ua2f7\ua2f8\ua2f9\ua2fa\ua2fb\ua2fc\ua2fd\ua2fe\ua2ff\ua300\ua301\ua302\ua303\ua304\ua305\ua306\ua307\ua308\ua309\ua30a\ua30b\ua30c\ua30d\ua30e\ua30f\ua310\ua311\ua312\ua313\ua314\ua315\ua316\ua317\ua318\ua319\ua31a\ua31b\ua31c\ua31d\ua31e\ua31f\ua320\ua321\ua322\ua323\ua324\ua325\ua326\ua327\ua328\ua329\ua32a\ua32b\ua32c\ua32d\ua32e\ua32f\ua330\ua331\ua332\ua333\ua334\ua335\ua336\ua337\ua338\ua339\ua33a\ua33b\ua33c\ua33d\ua33e\ua33f\ua340\ua341\ua342\ua343\ua344\ua345\ua346\ua347\ua348\ua349\ua34a\ua34b\ua34c\ua34d\ua34e\ua34f\ua350\ua351\ua352\ua353\ua354\ua355\ua356\ua357\ua358\ua359\ua35a\ua35b\ua35c\ua35d\ua35e\ua35f\ua360\ua361\ua362\ua363\ua364\ua365\ua366\ua367\ua368\ua369\ua36a\ua36b\ua36c\ua36d\ua36e\ua36f\ua370\ua371\ua372\ua373\ua374\ua375\ua376\ua377\ua378\ua379\ua37a\ua37b\ua37c\ua37d\ua37e\ua37f\ua380\ua381\ua382\ua383\ua384\ua385\ua386\ua387\ua388\ua389\ua38a\ua38b\ua38c\ua38d\ua38e\ua38f\ua390\ua391\ua392\ua393\ua394\ua395\ua396\ua397\ua398\ua399\ua39a\ua39b\ua39c\ua39d\ua39e\ua39f\ua3a0\ua3a1\ua3a2\ua3a3\ua3a4\ua3a5\ua3a6\ua3a7\ua3a8\ua3a9\ua3aa\ua3ab\ua3ac\ua3ad\ua3ae\ua3af\ua3b0\ua3b1\ua3b2\ua3b3\ua3b4\ua3b5\ua3b6\ua3b7\ua3b8\ua3b9\ua3ba\ua3bb\ua3bc\ua3bd\ua3be\ua3bf\ua3c0\ua3c1\ua3c2\ua3c3\ua3c4\ua3c5\ua3c6\ua3c7\ua3c8\ua3c9\ua3ca\ua3cb\ua3cc\ua3cd\ua3ce\ua3cf\ua3d0\ua3d1\ua3d2\ua3d3\ua3d4\ua3d5\ua3d6\ua3d7\ua3d8\ua3d9\ua3da\ua3db\ua3dc\ua3dd\ua3de\ua3df\ua3e0\ua3e1\ua3e2\ua3e3\ua3e4\ua3e5\ua3e6\ua3e7\ua3e8\ua3e9\ua3ea\ua3eb\ua3ec\ua3ed\ua3ee\ua3ef\ua3f0\ua3f1\ua3f2\ua3f3\ua3f4\ua3f5\ua3f6\ua3f7\ua3f8\ua3f9\ua3fa\ua3fb\ua3fc\ua3fd\ua3fe\ua3ff\ua400\ua401\ua402\ua403\ua404\ua405\ua406\ua407\ua408\ua409\ua40a\ua40b\ua40c\ua40d\ua40e\ua40f\ua410\ua411\ua412\ua413\ua414\ua415\ua416\ua417\ua418\ua419\ua41a\ua41b\ua41c\ua41d\ua41e\ua41f\ua420\ua421\ua422\ua423\ua424\ua425\ua426\ua427\ua428\ua429\ua42a\ua42b\ua42c\ua42d\ua42e\ua42f\ua430\ua431\ua432\ua433\ua434\ua435\ua436\ua437\ua438\ua439\ua43a\ua43b\ua43c\ua43d\ua43e\ua43f\ua440\ua441\ua442\ua443\ua444\ua445\ua446\ua447\ua448\ua449\ua44a\ua44b\ua44c\ua44d\ua44e\ua44f\ua450\ua451\ua452\ua453\ua454\ua455\ua456\ua457\ua458\ua459\ua45a\ua45b\ua45c\ua45d\ua45e\ua45f\ua460\ua461\ua462\ua463\ua464\ua465\ua466\ua467\ua468\ua469\ua46a\ua46b\ua46c\ua46d\ua46e\ua46f\ua470\ua471\ua472\ua473\ua474\ua475\ua476\ua477\ua478\ua479\ua47a\ua47b\ua47c\ua47d\ua47e\ua47f\ua480\ua481\ua482\ua483\ua484\ua485\ua486\ua487\ua488\ua489\ua48a\ua48b\ua48c\ua800\ua801\ua803\ua804\ua805\ua807\ua808\ua809\ua80a\ua80c\ua80d\ua80e\ua80f\ua810\ua811\ua812\ua813\ua814\ua815\ua816\ua817\ua818\ua819\ua81a\ua81b\ua81c\ua81d\ua81e\ua81f\ua820\ua821\ua822\uac00\uac01\uac02\uac03\uac04\uac05\uac06\uac07\uac08\uac09\uac0a\uac0b\uac0c\uac0d\uac0e\uac0f\uac10\uac11\uac12\uac13\uac14\uac15\uac16\uac17\uac18\uac19\uac1a\uac1b\uac1c\uac1d\uac1e\uac1f\uac20\uac21\uac22\uac23\uac24\uac25\uac26\uac27\uac28\uac29\uac2a\uac2b\uac2c\uac2d\uac2e\uac2f\uac30\uac31\uac32\uac33\uac34\uac35\uac36\uac37\uac38\uac39\uac3a\uac3b\uac3c\uac3d\uac3e\uac3f\uac40\uac41\uac42\uac43\uac44\uac45\uac46\uac47\uac48\uac49\uac4a\uac4b\uac4c\uac4d\uac4e\uac4f\uac50\uac51\uac52\uac53\uac54\uac55\uac56\uac57\uac58\uac59\uac5a\uac5b\uac5c\uac5d\uac5e\uac5f\uac60\uac61\uac62\uac63\uac64\uac65\uac66\uac67\uac68\uac69\uac6a\uac6b\uac6c\uac6d\uac6e\uac6f\uac70\uac71\uac72\uac73\uac74\uac75\uac76\uac77\uac78\uac79\uac7a\uac7b\uac7c\uac7d\uac7e\uac7f\uac80\uac81\uac82\uac83\uac84\uac85\uac86\uac87\uac88\uac89\uac8a\uac8b\uac8c\uac8d\uac8e\uac8f\uac90\uac91\uac92\uac93\uac94\uac95\uac96\uac97\uac98\uac99\uac9a\uac9b\uac9c\uac9d\uac9e\uac9f\uaca0\uaca1\uaca2\uaca3\uaca4\uaca5\uaca6\uaca7\uaca8\uaca9\uacaa\uacab\uacac\uacad\uacae\uacaf\uacb0\uacb1\uacb2\uacb3\uacb4\uacb5\uacb6\uacb7\uacb8\uacb9\uacba\uacbb\uacbc\uacbd\uacbe\uacbf\uacc0\uacc1\uacc2\uacc3\uacc4\uacc5\uacc6\uacc7\uacc8\uacc9\uacca\uaccb\uaccc\uaccd\uacce\uaccf\uacd0\uacd1\uacd2\uacd3\uacd4\uacd5\uacd6\uacd7\uacd8\uacd9\uacda\uacdb\uacdc\uacdd\uacde\uacdf\uace0\uace1\uace2\uace3\uace4\uace5\uace6\uace7\uace8\uace9\uacea\uaceb\uacec\uaced\uacee\uacef\uacf0\uacf1\uacf2\uacf3\uacf4\uacf5\uacf6\uacf7\uacf8\uacf9\uacfa\uacfb\uacfc\uacfd\uacfe\uacff\uad00\uad01\uad02\uad03\uad04\uad05\uad06\uad07\uad08\uad09\uad0a\uad0b\uad0c\uad0d\uad0e\uad0f\uad10\uad11\uad12\uad13\uad14\uad15\uad16\uad17\uad18\uad19\uad1a\uad1b\uad1c\uad1d\uad1e\uad1f\uad20\uad21\uad22\uad23\uad24\uad25\uad26\uad27\uad28\uad29\uad2a\uad2b\uad2c\uad2d\uad2e\uad2f\uad30\uad31\uad32\uad33\uad34\uad35\uad36\uad37\uad38\uad39\uad3a\uad3b\uad3c\uad3d\uad3e\uad3f\uad40\uad41\uad42\uad43\uad44\uad45\uad46\uad47\uad48\uad49\uad4a\uad4b\uad4c\uad4d\uad4e\uad4f\uad50\uad51\uad52\uad53\uad54\uad55\uad56\uad57\uad58\uad59\uad5a\uad5b\uad5c\uad5d\uad5e\uad5f\uad60\uad61\uad62\uad63\uad64\uad65\uad66\uad67\uad68\uad69\uad6a\uad6b\uad6c\uad6d\uad6e\uad6f\uad70\uad71\uad72\uad73\uad74\uad75\uad76\uad77\uad78\uad79\uad7a\uad7b\uad7c\uad7d\uad7e\uad7f\uad80\uad81\uad82\uad83\uad84\uad85\uad86\uad87\uad88\uad89\uad8a\uad8b\uad8c\uad8d\uad8e\uad8f\uad90\uad91\uad92\uad93\uad94\uad95\uad96\uad97\uad98\uad99\uad9a\uad9b\uad9c\uad9d\uad9e\uad9f\uada0\uada1\uada2\uada3\uada4\uada5\uada6\uada7\uada8\uada9\uadaa\uadab\uadac\uadad\uadae\uadaf\uadb0\uadb1\uadb2\uadb3\uadb4\uadb5\uadb6\uadb7\uadb8\uadb9\uadba\uadbb\uadbc\uadbd\uadbe\uadbf\uadc0\uadc1\uadc2\uadc3\uadc4\uadc5\uadc6\uadc7\uadc8\uadc9\uadca\uadcb\uadcc\uadcd\uadce\uadcf\uadd0\uadd1\uadd2\uadd3\uadd4\uadd5\uadd6\uadd7\uadd8\uadd9\uadda\uaddb\uaddc\uaddd\uadde\uaddf\uade0\uade1\uade2\uade3\uade4\uade5\uade6\uade7\uade8\uade9\uadea\uadeb\uadec\uaded\uadee\uadef\uadf0\uadf1\uadf2\uadf3\uadf4\uadf5\uadf6\uadf7\uadf8\uadf9\uadfa\uadfb\uadfc\uadfd\uadfe\uadff\uae00\uae01\uae02\uae03\uae04\uae05\uae06\uae07\uae08\uae09\uae0a\uae0b\uae0c\uae0d\uae0e\uae0f\uae10\uae11\uae12\uae13\uae14\uae15\uae16\uae17\uae18\uae19\uae1a\uae1b\uae1c\uae1d\uae1e\uae1f\uae20\uae21\uae22\uae23\uae24\uae25\uae26\uae27\uae28\uae29\uae2a\uae2b\uae2c\uae2d\uae2e\uae2f\uae30\uae31\uae32\uae33\uae34\uae35\uae36\uae37\uae38\uae39\uae3a\uae3b\uae3c\uae3d\uae3e\uae3f\uae40\uae41\uae42\uae43\uae44\uae45\uae46\uae47\uae48\uae49\uae4a\uae4b\uae4c\uae4d\uae4e\uae4f\uae50\uae51\uae52\uae53\uae54\uae55\uae56\uae57\uae58\uae59\uae5a\uae5b\uae5c\uae5d\uae5e\uae5f\uae60\uae61\uae62\uae63\uae64\uae65\uae66\uae67\uae68\uae69\uae6a\uae6b\uae6c\uae6d\uae6e\uae6f\uae70\uae71\uae72\uae73\uae74\uae75\uae76\uae77\uae78\uae79\uae7a\uae7b\uae7c\uae7d\uae7e\uae7f\uae80\uae81\uae82\uae83\uae84\uae85\uae86\uae87\uae88\uae89\uae8a\uae8b\uae8c\uae8d\uae8e\uae8f\uae90\uae91\uae92\uae93\uae94\uae95\uae96\uae97\uae98\uae99\uae9a\uae9b\uae9c\uae9d\uae9e\uae9f\uaea0\uaea1\uaea2\uaea3\uaea4\uaea5\uaea6\uaea7\uaea8\uaea9\uaeaa\uaeab\uaeac\uaead\uaeae\uaeaf\uaeb0\uaeb1\uaeb2\uaeb3\uaeb4\uaeb5\uaeb6\uaeb7\uaeb8\uaeb9\uaeba\uaebb\uaebc\uaebd\uaebe\uaebf\uaec0\uaec1\uaec2\uaec3\uaec4\uaec5\uaec6\uaec7\uaec8\uaec9\uaeca\uaecb\uaecc\uaecd\uaece\uaecf\uaed0\uaed1\uaed2\uaed3\uaed4\uaed5\uaed6\uaed7\uaed8\uaed9\uaeda\uaedb\uaedc\uaedd\uaede\uaedf\uaee0\uaee1\uaee2\uaee3\uaee4\uaee5\uaee6\uaee7\uaee8\uaee9\uaeea\uaeeb\uaeec\uaeed\uaeee\uaeef\uaef0\uaef1\uaef2\uaef3\uaef4\uaef5\uaef6\uaef7\uaef8\uaef9\uaefa\uaefb\uaefc\uaefd\uaefe\uaeff\uaf00\uaf01\uaf02\uaf03\uaf04\uaf05\uaf06\uaf07\uaf08\uaf09\uaf0a\uaf0b\uaf0c\uaf0d\uaf0e\uaf0f\uaf10\uaf11\uaf12\uaf13\uaf14\uaf15\uaf16\uaf17\uaf18\uaf19\uaf1a\uaf1b\uaf1c\uaf1d\uaf1e\uaf1f\uaf20\uaf21\uaf22\uaf23\uaf24\uaf25\uaf26\uaf27\uaf28\uaf29\uaf2a\uaf2b\uaf2c\uaf2d\uaf2e\uaf2f\uaf30\uaf31\uaf32\uaf33\uaf34\uaf35\uaf36\uaf37\uaf38\uaf39\uaf3a\uaf3b\uaf3c\uaf3d\uaf3e\uaf3f\uaf40\uaf41\uaf42\uaf43\uaf44\uaf45\uaf46\uaf47\uaf48\uaf49\uaf4a\uaf4b\uaf4c\uaf4d\uaf4e\uaf4f\uaf50\uaf51\uaf52\uaf53\uaf54\uaf55\uaf56\uaf57\uaf58\uaf59\uaf5a\uaf5b\uaf5c\uaf5d\uaf5e\uaf5f\uaf60\uaf61\uaf62\uaf63\uaf64\uaf65\uaf66\uaf67\uaf68\uaf69\uaf6a\uaf6b\uaf6c\uaf6d\uaf6e\uaf6f\uaf70\uaf71\uaf72\uaf73\uaf74\uaf75\uaf76\uaf77\uaf78\uaf79\uaf7a\uaf7b\uaf7c\uaf7d\uaf7e\uaf7f\uaf80\uaf81\uaf82\uaf83\uaf84\uaf85\uaf86\uaf87\uaf88\uaf89\uaf8a\uaf8b\uaf8c\uaf8d\uaf8e\uaf8f\uaf90\uaf91\uaf92\uaf93\uaf94\uaf95\uaf96\uaf97\uaf98\uaf99\uaf9a\uaf9b\uaf9c\uaf9d\uaf9e\uaf9f\uafa0\uafa1\uafa2\uafa3\uafa4\uafa5\uafa6\uafa7\uafa8\uafa9\uafaa\uafab\uafac\uafad\uafae\uafaf\uafb0\uafb1\uafb2\uafb3\uafb4\uafb5\uafb6\uafb7\uafb8\uafb9\uafba\uafbb\uafbc\uafbd\uafbe\uafbf\uafc0\uafc1\uafc2\uafc3\uafc4\uafc5\uafc6\uafc7\uafc8\uafc9\uafca\uafcb\uafcc\uafcd\uafce\uafcf\uafd0\uafd1\uafd2\uafd3\uafd4\uafd5\uafd6\uafd7\uafd8\uafd9\uafda\uafdb\uafdc\uafdd\uafde\uafdf\uafe0\uafe1\uafe2\uafe3\uafe4\uafe5\uafe6\uafe7\uafe8\uafe9\uafea\uafeb\uafec\uafed\uafee\uafef\uaff0\uaff1\uaff2\uaff3\uaff4\uaff5\uaff6\uaff7\uaff8\uaff9\uaffa\uaffb\uaffc\uaffd\uaffe\uafff\ub000\ub001\ub002\ub003\ub004\ub005\ub006\ub007\ub008\ub009\ub00a\ub00b\ub00c\ub00d\ub00e\ub00f\ub010\ub011\ub012\ub013\ub014\ub015\ub016\ub017\ub018\ub019\ub01a\ub01b\ub01c\ub01d\ub01e\ub01f\ub020\ub021\ub022\ub023\ub024\ub025\ub026\ub027\ub028\ub029\ub02a\ub02b\ub02c\ub02d\ub02e\ub02f\ub030\ub031\ub032\ub033\ub034\ub035\ub036\ub037\ub038\ub039\ub03a\ub03b\ub03c\ub03d\ub03e\ub03f\ub040\ub041\ub042\ub043\ub044\ub045\ub046\ub047\ub048\ub049\ub04a\ub04b\ub04c\ub04d\ub04e\ub04f\ub050\ub051\ub052\ub053\ub054\ub055\ub056\ub057\ub058\ub059\ub05a\ub05b\ub05c\ub05d\ub05e\ub05f\ub060\ub061\ub062\ub063\ub064\ub065\ub066\ub067\ub068\ub069\ub06a\ub06b\ub06c\ub06d\ub06e\ub06f\ub070\ub071\ub072\ub073\ub074\ub075\ub076\ub077\ub078\ub079\ub07a\ub07b\ub07c\ub07d\ub07e\ub07f\ub080\ub081\ub082\ub083\ub084\ub085\ub086\ub087\ub088\ub089\ub08a\ub08b\ub08c\ub08d\ub08e\ub08f\ub090\ub091\ub092\ub093\ub094\ub095\ub096\ub097\ub098\ub099\ub09a\ub09b\ub09c\ub09d\ub09e\ub09f\ub0a0\ub0a1\ub0a2\ub0a3\ub0a4\ub0a5\ub0a6\ub0a7\ub0a8\ub0a9\ub0aa\ub0ab\ub0ac\ub0ad\ub0ae\ub0af\ub0b0\ub0b1\ub0b2\ub0b3\ub0b4\ub0b5\ub0b6\ub0b7\ub0b8\ub0b9\ub0ba\ub0bb\ub0bc\ub0bd\ub0be\ub0bf\ub0c0\ub0c1\ub0c2\ub0c3\ub0c4\ub0c5\ub0c6\ub0c7\ub0c8\ub0c9\ub0ca\ub0cb\ub0cc\ub0cd\ub0ce\ub0cf\ub0d0\ub0d1\ub0d2\ub0d3\ub0d4\ub0d5\ub0d6\ub0d7\ub0d8\ub0d9\ub0da\ub0db\ub0dc\ub0dd\ub0de\ub0df\ub0e0\ub0e1\ub0e2\ub0e3\ub0e4\ub0e5\ub0e6\ub0e7\ub0e8\ub0e9\ub0ea\ub0eb\ub0ec\ub0ed\ub0ee\ub0ef\ub0f0\ub0f1\ub0f2\ub0f3\ub0f4\ub0f5\ub0f6\ub0f7\ub0f8\ub0f9\ub0fa\ub0fb\ub0fc\ub0fd\ub0fe\ub0ff\ub100\ub101\ub102\ub103\ub104\ub105\ub106\ub107\ub108\ub109\ub10a\ub10b\ub10c\ub10d\ub10e\ub10f\ub110\ub111\ub112\ub113\ub114\ub115\ub116\ub117\ub118\ub119\ub11a\ub11b\ub11c\ub11d\ub11e\ub11f\ub120\ub121\ub122\ub123\ub124\ub125\ub126\ub127\ub128\ub129\ub12a\ub12b\ub12c\ub12d\ub12e\ub12f\ub130\ub131\ub132\ub133\ub134\ub135\ub136\ub137\ub138\ub139\ub13a\ub13b\ub13c\ub13d\ub13e\ub13f\ub140\ub141\ub142\ub143\ub144\ub145\ub146\ub147\ub148\ub149\ub14a\ub14b\ub14c\ub14d\ub14e\ub14f\ub150\ub151\ub152\ub153\ub154\ub155\ub156\ub157\ub158\ub159\ub15a\ub15b\ub15c\ub15d\ub15e\ub15f\ub160\ub161\ub162\ub163\ub164\ub165\ub166\ub167\ub168\ub169\ub16a\ub16b\ub16c\ub16d\ub16e\ub16f\ub170\ub171\ub172\ub173\ub174\ub175\ub176\ub177\ub178\ub179\ub17a\ub17b\ub17c\ub17d\ub17e\ub17f\ub180\ub181\ub182\ub183\ub184\ub185\ub186\ub187\ub188\ub189\ub18a\ub18b\ub18c\ub18d\ub18e\ub18f\ub190\ub191\ub192\ub193\ub194\ub195\ub196\ub197\ub198\ub199\ub19a\ub19b\ub19c\ub19d\ub19e\ub19f\ub1a0\ub1a1\ub1a2\ub1a3\ub1a4\ub1a5\ub1a6\ub1a7\ub1a8\ub1a9\ub1aa\ub1ab\ub1ac\ub1ad\ub1ae\ub1af\ub1b0\ub1b1\ub1b2\ub1b3\ub1b4\ub1b5\ub1b6\ub1b7\ub1b8\ub1b9\ub1ba\ub1bb\ub1bc\ub1bd\ub1be\ub1bf\ub1c0\ub1c1\ub1c2\ub1c3\ub1c4\ub1c5\ub1c6\ub1c7\ub1c8\ub1c9\ub1ca\ub1cb\ub1cc\ub1cd\ub1ce\ub1cf\ub1d0\ub1d1\ub1d2\ub1d3\ub1d4\ub1d5\ub1d6\ub1d7\ub1d8\ub1d9\ub1da\ub1db\ub1dc\ub1dd\ub1de\ub1df\ub1e0\ub1e1\ub1e2\ub1e3\ub1e4\ub1e5\ub1e6\ub1e7\ub1e8\ub1e9\ub1ea\ub1eb\ub1ec\ub1ed\ub1ee\ub1ef\ub1f0\ub1f1\ub1f2\ub1f3\ub1f4\ub1f5\ub1f6\ub1f7\ub1f8\ub1f9\ub1fa\ub1fb\ub1fc\ub1fd\ub1fe\ub1ff\ub200\ub201\ub202\ub203\ub204\ub205\ub206\ub207\ub208\ub209\ub20a\ub20b\ub20c\ub20d\ub20e\ub20f\ub210\ub211\ub212\ub213\ub214\ub215\ub216\ub217\ub218\ub219\ub21a\ub21b\ub21c\ub21d\ub21e\ub21f\ub220\ub221\ub222\ub223\ub224\ub225\ub226\ub227\ub228\ub229\ub22a\ub22b\ub22c\ub22d\ub22e\ub22f\ub230\ub231\ub232\ub233\ub234\ub235\ub236\ub237\ub238\ub239\ub23a\ub23b\ub23c\ub23d\ub23e\ub23f\ub240\ub241\ub242\ub243\ub244\ub245\ub246\ub247\ub248\ub249\ub24a\ub24b\ub24c\ub24d\ub24e\ub24f\ub250\ub251\ub252\ub253\ub254\ub255\ub256\ub257\ub258\ub259\ub25a\ub25b\ub25c\ub25d\ub25e\ub25f\ub260\ub261\ub262\ub263\ub264\ub265\ub266\ub267\ub268\ub269\ub26a\ub26b\ub26c\ub26d\ub26e\ub26f\ub270\ub271\ub272\ub273\ub274\ub275\ub276\ub277\ub278\ub279\ub27a\ub27b\ub27c\ub27d\ub27e\ub27f\ub280\ub281\ub282\ub283\ub284\ub285\ub286\ub287\ub288\ub289\ub28a\ub28b\ub28c\ub28d\ub28e\ub28f\ub290\ub291\ub292\ub293\ub294\ub295\ub296\ub297\ub298\ub299\ub29a\ub29b\ub29c\ub29d\ub29e\ub29f\ub2a0\ub2a1\ub2a2\ub2a3\ub2a4\ub2a5\ub2a6\ub2a7\ub2a8\ub2a9\ub2aa\ub2ab\ub2ac\ub2ad\ub2ae\ub2af\ub2b0\ub2b1\ub2b2\ub2b3\ub2b4\ub2b5\ub2b6\ub2b7\ub2b8\ub2b9\ub2ba\ub2bb\ub2bc\ub2bd\ub2be\ub2bf\ub2c0\ub2c1\ub2c2\ub2c3\ub2c4\ub2c5\ub2c6\ub2c7\ub2c8\ub2c9\ub2ca\ub2cb\ub2cc\ub2cd\ub2ce\ub2cf\ub2d0\ub2d1\ub2d2\ub2d3\ub2d4\ub2d5\ub2d6\ub2d7\ub2d8\ub2d9\ub2da\ub2db\ub2dc\ub2dd\ub2de\ub2df\ub2e0\ub2e1\ub2e2\ub2e3\ub2e4\ub2e5\ub2e6\ub2e7\ub2e8\ub2e9\ub2ea\ub2eb\ub2ec\ub2ed\ub2ee\ub2ef\ub2f0\ub2f1\ub2f2\ub2f3\ub2f4\ub2f5\ub2f6\ub2f7\ub2f8\ub2f9\ub2fa\ub2fb\ub2fc\ub2fd\ub2fe\ub2ff\ub300\ub301\ub302\ub303\ub304\ub305\ub306\ub307\ub308\ub309\ub30a\ub30b\ub30c\ub30d\ub30e\ub30f\ub310\ub311\ub312\ub313\ub314\ub315\ub316\ub317\ub318\ub319\ub31a\ub31b\ub31c\ub31d\ub31e\ub31f\ub320\ub321\ub322\ub323\ub324\ub325\ub326\ub327\ub328\ub329\ub32a\ub32b\ub32c\ub32d\ub32e\ub32f\ub330\ub331\ub332\ub333\ub334\ub335\ub336\ub337\ub338\ub339\ub33a\ub33b\ub33c\ub33d\ub33e\ub33f\ub340\ub341\ub342\ub343\ub344\ub345\ub346\ub347\ub348\ub349\ub34a\ub34b\ub34c\ub34d\ub34e\ub34f\ub350\ub351\ub352\ub353\ub354\ub355\ub356\ub357\ub358\ub359\ub35a\ub35b\ub35c\ub35d\ub35e\ub35f\ub360\ub361\ub362\ub363\ub364\ub365\ub366\ub367\ub368\ub369\ub36a\ub36b\ub36c\ub36d\ub36e\ub36f\ub370\ub371\ub372\ub373\ub374\ub375\ub376\ub377\ub378\ub379\ub37a\ub37b\ub37c\ub37d\ub37e\ub37f\ub380\ub381\ub382\ub383\ub384\ub385\ub386\ub387\ub388\ub389\ub38a\ub38b\ub38c\ub38d\ub38e\ub38f\ub390\ub391\ub392\ub393\ub394\ub395\ub396\ub397\ub398\ub399\ub39a\ub39b\ub39c\ub39d\ub39e\ub39f\ub3a0\ub3a1\ub3a2\ub3a3\ub3a4\ub3a5\ub3a6\ub3a7\ub3a8\ub3a9\ub3aa\ub3ab\ub3ac\ub3ad\ub3ae\ub3af\ub3b0\ub3b1\ub3b2\ub3b3\ub3b4\ub3b5\ub3b6\ub3b7\ub3b8\ub3b9\ub3ba\ub3bb\ub3bc\ub3bd\ub3be\ub3bf\ub3c0\ub3c1\ub3c2\ub3c3\ub3c4\ub3c5\ub3c6\ub3c7\ub3c8\ub3c9\ub3ca\ub3cb\ub3cc\ub3cd\ub3ce\ub3cf\ub3d0\ub3d1\ub3d2\ub3d3\ub3d4\ub3d5\ub3d6\ub3d7\ub3d8\ub3d9\ub3da\ub3db\ub3dc\ub3dd\ub3de\ub3df\ub3e0\ub3e1\ub3e2\ub3e3\ub3e4\ub3e5\ub3e6\ub3e7\ub3e8\ub3e9\ub3ea\ub3eb\ub3ec\ub3ed\ub3ee\ub3ef\ub3f0\ub3f1\ub3f2\ub3f3\ub3f4\ub3f5\ub3f6\ub3f7\ub3f8\ub3f9\ub3fa\ub3fb\ub3fc\ub3fd\ub3fe\ub3ff\ub400\ub401\ub402\ub403\ub404\ub405\ub406\ub407\ub408\ub409\ub40a\ub40b\ub40c\ub40d\ub40e\ub40f\ub410\ub411\ub412\ub413\ub414\ub415\ub416\ub417\ub418\ub419\ub41a\ub41b\ub41c\ub41d\ub41e\ub41f\ub420\ub421\ub422\ub423\ub424\ub425\ub426\ub427\ub428\ub429\ub42a\ub42b\ub42c\ub42d\ub42e\ub42f\ub430\ub431\ub432\ub433\ub434\ub435\ub436\ub437\ub438\ub439\ub43a\ub43b\ub43c\ub43d\ub43e\ub43f\ub440\ub441\ub442\ub443\ub444\ub445\ub446\ub447\ub448\ub449\ub44a\ub44b\ub44c\ub44d\ub44e\ub44f\ub450\ub451\ub452\ub453\ub454\ub455\ub456\ub457\ub458\ub459\ub45a\ub45b\ub45c\ub45d\ub45e\ub45f\ub460\ub461\ub462\ub463\ub464\ub465\ub466\ub467\ub468\ub469\ub46a\ub46b\ub46c\ub46d\ub46e\ub46f\ub470\ub471\ub472\ub473\ub474\ub475\ub476\ub477\ub478\ub479\ub47a\ub47b\ub47c\ub47d\ub47e\ub47f\ub480\ub481\ub482\ub483\ub484\ub485\ub486\ub487\ub488\ub489\ub48a\ub48b\ub48c\ub48d\ub48e\ub48f\ub490\ub491\ub492\ub493\ub494\ub495\ub496\ub497\ub498\ub499\ub49a\ub49b\ub49c\ub49d\ub49e\ub49f\ub4a0\ub4a1\ub4a2\ub4a3\ub4a4\ub4a5\ub4a6\ub4a7\ub4a8\ub4a9\ub4aa\ub4ab\ub4ac\ub4ad\ub4ae\ub4af\ub4b0\ub4b1\ub4b2\ub4b3\ub4b4\ub4b5\ub4b6\ub4b7\ub4b8\ub4b9\ub4ba\ub4bb\ub4bc\ub4bd\ub4be\ub4bf\ub4c0\ub4c1\ub4c2\ub4c3\ub4c4\ub4c5\ub4c6\ub4c7\ub4c8\ub4c9\ub4ca\ub4cb\ub4cc\ub4cd\ub4ce\ub4cf\ub4d0\ub4d1\ub4d2\ub4d3\ub4d4\ub4d5\ub4d6\ub4d7\ub4d8\ub4d9\ub4da\ub4db\ub4dc\ub4dd\ub4de\ub4df\ub4e0\ub4e1\ub4e2\ub4e3\ub4e4\ub4e5\ub4e6\ub4e7\ub4e8\ub4e9\ub4ea\ub4eb\ub4ec\ub4ed\ub4ee\ub4ef\ub4f0\ub4f1\ub4f2\ub4f3\ub4f4\ub4f5\ub4f6\ub4f7\ub4f8\ub4f9\ub4fa\ub4fb\ub4fc\ub4fd\ub4fe\ub4ff\ub500\ub501\ub502\ub503\ub504\ub505\ub506\ub507\ub508\ub509\ub50a\ub50b\ub50c\ub50d\ub50e\ub50f\ub510\ub511\ub512\ub513\ub514\ub515\ub516\ub517\ub518\ub519\ub51a\ub51b\ub51c\ub51d\ub51e\ub51f\ub520\ub521\ub522\ub523\ub524\ub525\ub526\ub527\ub528\ub529\ub52a\ub52b\ub52c\ub52d\ub52e\ub52f\ub530\ub531\ub532\ub533\ub534\ub535\ub536\ub537\ub538\ub539\ub53a\ub53b\ub53c\ub53d\ub53e\ub53f\ub540\ub541\ub542\ub543\ub544\ub545\ub546\ub547\ub548\ub549\ub54a\ub54b\ub54c\ub54d\ub54e\ub54f\ub550\ub551\ub552\ub553\ub554\ub555\ub556\ub557\ub558\ub559\ub55a\ub55b\ub55c\ub55d\ub55e\ub55f\ub560\ub561\ub562\ub563\ub564\ub565\ub566\ub567\ub568\ub569\ub56a\ub56b\ub56c\ub56d\ub56e\ub56f\ub570\ub571\ub572\ub573\ub574\ub575\ub576\ub577\ub578\ub579\ub57a\ub57b\ub57c\ub57d\ub57e\ub57f\ub580\ub581\ub582\ub583\ub584\ub585\ub586\ub587\ub588\ub589\ub58a\ub58b\ub58c\ub58d\ub58e\ub58f\ub590\ub591\ub592\ub593\ub594\ub595\ub596\ub597\ub598\ub599\ub59a\ub59b\ub59c\ub59d\ub59e\ub59f\ub5a0\ub5a1\ub5a2\ub5a3\ub5a4\ub5a5\ub5a6\ub5a7\ub5a8\ub5a9\ub5aa\ub5ab\ub5ac\ub5ad\ub5ae\ub5af\ub5b0\ub5b1\ub5b2\ub5b3\ub5b4\ub5b5\ub5b6\ub5b7\ub5b8\ub5b9\ub5ba\ub5bb\ub5bc\ub5bd\ub5be\ub5bf\ub5c0\ub5c1\ub5c2\ub5c3\ub5c4\ub5c5\ub5c6\ub5c7\ub5c8\ub5c9\ub5ca\ub5cb\ub5cc\ub5cd\ub5ce\ub5cf\ub5d0\ub5d1\ub5d2\ub5d3\ub5d4\ub5d5\ub5d6\ub5d7\ub5d8\ub5d9\ub5da\ub5db\ub5dc\ub5dd\ub5de\ub5df\ub5e0\ub5e1\ub5e2\ub5e3\ub5e4\ub5e5\ub5e6\ub5e7\ub5e8\ub5e9\ub5ea\ub5eb\ub5ec\ub5ed\ub5ee\ub5ef\ub5f0\ub5f1\ub5f2\ub5f3\ub5f4\ub5f5\ub5f6\ub5f7\ub5f8\ub5f9\ub5fa\ub5fb\ub5fc\ub5fd\ub5fe\ub5ff\ub600\ub601\ub602\ub603\ub604\ub605\ub606\ub607\ub608\ub609\ub60a\ub60b\ub60c\ub60d\ub60e\ub60f\ub610\ub611\ub612\ub613\ub614\ub615\ub616\ub617\ub618\ub619\ub61a\ub61b\ub61c\ub61d\ub61e\ub61f\ub620\ub621\ub622\ub623\ub624\ub625\ub626\ub627\ub628\ub629\ub62a\ub62b\ub62c\ub62d\ub62e\ub62f\ub630\ub631\ub632\ub633\ub634\ub635\ub636\ub637\ub638\ub639\ub63a\ub63b\ub63c\ub63d\ub63e\ub63f\ub640\ub641\ub642\ub643\ub644\ub645\ub646\ub647\ub648\ub649\ub64a\ub64b\ub64c\ub64d\ub64e\ub64f\ub650\ub651\ub652\ub653\ub654\ub655\ub656\ub657\ub658\ub659\ub65a\ub65b\ub65c\ub65d\ub65e\ub65f\ub660\ub661\ub662\ub663\ub664\ub665\ub666\ub667\ub668\ub669\ub66a\ub66b\ub66c\ub66d\ub66e\ub66f\ub670\ub671\ub672\ub673\ub674\ub675\ub676\ub677\ub678\ub679\ub67a\ub67b\ub67c\ub67d\ub67e\ub67f\ub680\ub681\ub682\ub683\ub684\ub685\ub686\ub687\ub688\ub689\ub68a\ub68b\ub68c\ub68d\ub68e\ub68f\ub690\ub691\ub692\ub693\ub694\ub695\ub696\ub697\ub698\ub699\ub69a\ub69b\ub69c\ub69d\ub69e\ub69f\ub6a0\ub6a1\ub6a2\ub6a3\ub6a4\ub6a5\ub6a6\ub6a7\ub6a8\ub6a9\ub6aa\ub6ab\ub6ac\ub6ad\ub6ae\ub6af\ub6b0\ub6b1\ub6b2\ub6b3\ub6b4\ub6b5\ub6b6\ub6b7\ub6b8\ub6b9\ub6ba\ub6bb\ub6bc\ub6bd\ub6be\ub6bf\ub6c0\ub6c1\ub6c2\ub6c3\ub6c4\ub6c5\ub6c6\ub6c7\ub6c8\ub6c9\ub6ca\ub6cb\ub6cc\ub6cd\ub6ce\ub6cf\ub6d0\ub6d1\ub6d2\ub6d3\ub6d4\ub6d5\ub6d6\ub6d7\ub6d8\ub6d9\ub6da\ub6db\ub6dc\ub6dd\ub6de\ub6df\ub6e0\ub6e1\ub6e2\ub6e3\ub6e4\ub6e5\ub6e6\ub6e7\ub6e8\ub6e9\ub6ea\ub6eb\ub6ec\ub6ed\ub6ee\ub6ef\ub6f0\ub6f1\ub6f2\ub6f3\ub6f4\ub6f5\ub6f6\ub6f7\ub6f8\ub6f9\ub6fa\ub6fb\ub6fc\ub6fd\ub6fe\ub6ff\ub700\ub701\ub702\ub703\ub704\ub705\ub706\ub707\ub708\ub709\ub70a\ub70b\ub70c\ub70d\ub70e\ub70f\ub710\ub711\ub712\ub713\ub714\ub715\ub716\ub717\ub718\ub719\ub71a\ub71b\ub71c\ub71d\ub71e\ub71f\ub720\ub721\ub722\ub723\ub724\ub725\ub726\ub727\ub728\ub729\ub72a\ub72b\ub72c\ub72d\ub72e\ub72f\ub730\ub731\ub732\ub733\ub734\ub735\ub736\ub737\ub738\ub739\ub73a\ub73b\ub73c\ub73d\ub73e\ub73f\ub740\ub741\ub742\ub743\ub744\ub745\ub746\ub747\ub748\ub749\ub74a\ub74b\ub74c\ub74d\ub74e\ub74f\ub750\ub751\ub752\ub753\ub754\ub755\ub756\ub757\ub758\ub759\ub75a\ub75b\ub75c\ub75d\ub75e\ub75f\ub760\ub761\ub762\ub763\ub764\ub765\ub766\ub767\ub768\ub769\ub76a\ub76b\ub76c\ub76d\ub76e\ub76f\ub770\ub771\ub772\ub773\ub774\ub775\ub776\ub777\ub778\ub779\ub77a\ub77b\ub77c\ub77d\ub77e\ub77f\ub780\ub781\ub782\ub783\ub784\ub785\ub786\ub787\ub788\ub789\ub78a\ub78b\ub78c\ub78d\ub78e\ub78f\ub790\ub791\ub792\ub793\ub794\ub795\ub796\ub797\ub798\ub799\ub79a\ub79b\ub79c\ub79d\ub79e\ub79f\ub7a0\ub7a1\ub7a2\ub7a3\ub7a4\ub7a5\ub7a6\ub7a7\ub7a8\ub7a9\ub7aa\ub7ab\ub7ac\ub7ad\ub7ae\ub7af\ub7b0\ub7b1\ub7b2\ub7b3\ub7b4\ub7b5\ub7b6\ub7b7\ub7b8\ub7b9\ub7ba\ub7bb\ub7bc\ub7bd\ub7be\ub7bf\ub7c0\ub7c1\ub7c2\ub7c3\ub7c4\ub7c5\ub7c6\ub7c7\ub7c8\ub7c9\ub7ca\ub7cb\ub7cc\ub7cd\ub7ce\ub7cf\ub7d0\ub7d1\ub7d2\ub7d3\ub7d4\ub7d5\ub7d6\ub7d7\ub7d8\ub7d9\ub7da\ub7db\ub7dc\ub7dd\ub7de\ub7df\ub7e0\ub7e1\ub7e2\ub7e3\ub7e4\ub7e5\ub7e6\ub7e7\ub7e8\ub7e9\ub7ea\ub7eb\ub7ec\ub7ed\ub7ee\ub7ef\ub7f0\ub7f1\ub7f2\ub7f3\ub7f4\ub7f5\ub7f6\ub7f7\ub7f8\ub7f9\ub7fa\ub7fb\ub7fc\ub7fd\ub7fe\ub7ff\ub800\ub801\ub802\ub803\ub804\ub805\ub806\ub807\ub808\ub809\ub80a\ub80b\ub80c\ub80d\ub80e\ub80f\ub810\ub811\ub812\ub813\ub814\ub815\ub816\ub817\ub818\ub819\ub81a\ub81b\ub81c\ub81d\ub81e\ub81f\ub820\ub821\ub822\ub823\ub824\ub825\ub826\ub827\ub828\ub829\ub82a\ub82b\ub82c\ub82d\ub82e\ub82f\ub830\ub831\ub832\ub833\ub834\ub835\ub836\ub837\ub838\ub839\ub83a\ub83b\ub83c\ub83d\ub83e\ub83f\ub840\ub841\ub842\ub843\ub844\ub845\ub846\ub847\ub848\ub849\ub84a\ub84b\ub84c\ub84d\ub84e\ub84f\ub850\ub851\ub852\ub853\ub854\ub855\ub856\ub857\ub858\ub859\ub85a\ub85b\ub85c\ub85d\ub85e\ub85f\ub860\ub861\ub862\ub863\ub864\ub865\ub866\ub867\ub868\ub869\ub86a\ub86b\ub86c\ub86d\ub86e\ub86f\ub870\ub871\ub872\ub873\ub874\ub875\ub876\ub877\ub878\ub879\ub87a\ub87b\ub87c\ub87d\ub87e\ub87f\ub880\ub881\ub882\ub883\ub884\ub885\ub886\ub887\ub888\ub889\ub88a\ub88b\ub88c\ub88d\ub88e\ub88f\ub890\ub891\ub892\ub893\ub894\ub895\ub896\ub897\ub898\ub899\ub89a\ub89b\ub89c\ub89d\ub89e\ub89f\ub8a0\ub8a1\ub8a2\ub8a3\ub8a4\ub8a5\ub8a6\ub8a7\ub8a8\ub8a9\ub8aa\ub8ab\ub8ac\ub8ad\ub8ae\ub8af\ub8b0\ub8b1\ub8b2\ub8b3\ub8b4\ub8b5\ub8b6\ub8b7\ub8b8\ub8b9\ub8ba\ub8bb\ub8bc\ub8bd\ub8be\ub8bf\ub8c0\ub8c1\ub8c2\ub8c3\ub8c4\ub8c5\ub8c6\ub8c7\ub8c8\ub8c9\ub8ca\ub8cb\ub8cc\ub8cd\ub8ce\ub8cf\ub8d0\ub8d1\ub8d2\ub8d3\ub8d4\ub8d5\ub8d6\ub8d7\ub8d8\ub8d9\ub8da\ub8db\ub8dc\ub8dd\ub8de\ub8df\ub8e0\ub8e1\ub8e2\ub8e3\ub8e4\ub8e5\ub8e6\ub8e7\ub8e8\ub8e9\ub8ea\ub8eb\ub8ec\ub8ed\ub8ee\ub8ef\ub8f0\ub8f1\ub8f2\ub8f3\ub8f4\ub8f5\ub8f6\ub8f7\ub8f8\ub8f9\ub8fa\ub8fb\ub8fc\ub8fd\ub8fe\ub8ff\ub900\ub901\ub902\ub903\ub904\ub905\ub906\ub907\ub908\ub909\ub90a\ub90b\ub90c\ub90d\ub90e\ub90f\ub910\ub911\ub912\ub913\ub914\ub915\ub916\ub917\ub918\ub919\ub91a\ub91b\ub91c\ub91d\ub91e\ub91f\ub920\ub921\ub922\ub923\ub924\ub925\ub926\ub927\ub928\ub929\ub92a\ub92b\ub92c\ub92d\ub92e\ub92f\ub930\ub931\ub932\ub933\ub934\ub935\ub936\ub937\ub938\ub939\ub93a\ub93b\ub93c\ub93d\ub93e\ub93f\ub940\ub941\ub942\ub943\ub944\ub945\ub946\ub947\ub948\ub949\ub94a\ub94b\ub94c\ub94d\ub94e\ub94f\ub950\ub951\ub952\ub953\ub954\ub955\ub956\ub957\ub958\ub959\ub95a\ub95b\ub95c\ub95d\ub95e\ub95f\ub960\ub961\ub962\ub963\ub964\ub965\ub966\ub967\ub968\ub969\ub96a\ub96b\ub96c\ub96d\ub96e\ub96f\ub970\ub971\ub972\ub973\ub974\ub975\ub976\ub977\ub978\ub979\ub97a\ub97b\ub97c\ub97d\ub97e\ub97f\ub980\ub981\ub982\ub983\ub984\ub985\ub986\ub987\ub988\ub989\ub98a\ub98b\ub98c\ub98d\ub98e\ub98f\ub990\ub991\ub992\ub993\ub994\ub995\ub996\ub997\ub998\ub999\ub99a\ub99b\ub99c\ub99d\ub99e\ub99f\ub9a0\ub9a1\ub9a2\ub9a3\ub9a4\ub9a5\ub9a6\ub9a7\ub9a8\ub9a9\ub9aa\ub9ab\ub9ac\ub9ad\ub9ae\ub9af\ub9b0\ub9b1\ub9b2\ub9b3\ub9b4\ub9b5\ub9b6\ub9b7\ub9b8\ub9b9\ub9ba\ub9bb\ub9bc\ub9bd\ub9be\ub9bf\ub9c0\ub9c1\ub9c2\ub9c3\ub9c4\ub9c5\ub9c6\ub9c7\ub9c8\ub9c9\ub9ca\ub9cb\ub9cc\ub9cd\ub9ce\ub9cf\ub9d0\ub9d1\ub9d2\ub9d3\ub9d4\ub9d5\ub9d6\ub9d7\ub9d8\ub9d9\ub9da\ub9db\ub9dc\ub9dd\ub9de\ub9df\ub9e0\ub9e1\ub9e2\ub9e3\ub9e4\ub9e5\ub9e6\ub9e7\ub9e8\ub9e9\ub9ea\ub9eb\ub9ec\ub9ed\ub9ee\ub9ef\ub9f0\ub9f1\ub9f2\ub9f3\ub9f4\ub9f5\ub9f6\ub9f7\ub9f8\ub9f9\ub9fa\ub9fb\ub9fc\ub9fd\ub9fe\ub9ff\uba00\uba01\uba02\uba03\uba04\uba05\uba06\uba07\uba08\uba09\uba0a\uba0b\uba0c\uba0d\uba0e\uba0f\uba10\uba11\uba12\uba13\uba14\uba15\uba16\uba17\uba18\uba19\uba1a\uba1b\uba1c\uba1d\uba1e\uba1f\uba20\uba21\uba22\uba23\uba24\uba25\uba26\uba27\uba28\uba29\uba2a\uba2b\uba2c\uba2d\uba2e\uba2f\uba30\uba31\uba32\uba33\uba34\uba35\uba36\uba37\uba38\uba39\uba3a\uba3b\uba3c\uba3d\uba3e\uba3f\uba40\uba41\uba42\uba43\uba44\uba45\uba46\uba47\uba48\uba49\uba4a\uba4b\uba4c\uba4d\uba4e\uba4f\uba50\uba51\uba52\uba53\uba54\uba55\uba56\uba57\uba58\uba59\uba5a\uba5b\uba5c\uba5d\uba5e\uba5f\uba60\uba61\uba62\uba63\uba64\uba65\uba66\uba67\uba68\uba69\uba6a\uba6b\uba6c\uba6d\uba6e\uba6f\uba70\uba71\uba72\uba73\uba74\uba75\uba76\uba77\uba78\uba79\uba7a\uba7b\uba7c\uba7d\uba7e\uba7f\uba80\uba81\uba82\uba83\uba84\uba85\uba86\uba87\uba88\uba89\uba8a\uba8b\uba8c\uba8d\uba8e\uba8f\uba90\uba91\uba92\uba93\uba94\uba95\uba96\uba97\uba98\uba99\uba9a\uba9b\uba9c\uba9d\uba9e\uba9f\ubaa0\ubaa1\ubaa2\ubaa3\ubaa4\ubaa5\ubaa6\ubaa7\ubaa8\ubaa9\ubaaa\ubaab\ubaac\ubaad\ubaae\ubaaf\ubab0\ubab1\ubab2\ubab3\ubab4\ubab5\ubab6\ubab7\ubab8\ubab9\ubaba\ubabb\ubabc\ubabd\ubabe\ubabf\ubac0\ubac1\ubac2\ubac3\ubac4\ubac5\ubac6\ubac7\ubac8\ubac9\ubaca\ubacb\ubacc\ubacd\ubace\ubacf\ubad0\ubad1\ubad2\ubad3\ubad4\ubad5\ubad6\ubad7\ubad8\ubad9\ubada\ubadb\ubadc\ubadd\ubade\ubadf\ubae0\ubae1\ubae2\ubae3\ubae4\ubae5\ubae6\ubae7\ubae8\ubae9\ubaea\ubaeb\ubaec\ubaed\ubaee\ubaef\ubaf0\ubaf1\ubaf2\ubaf3\ubaf4\ubaf5\ubaf6\ubaf7\ubaf8\ubaf9\ubafa\ubafb\ubafc\ubafd\ubafe\ubaff\ubb00\ubb01\ubb02\ubb03\ubb04\ubb05\ubb06\ubb07\ubb08\ubb09\ubb0a\ubb0b\ubb0c\ubb0d\ubb0e\ubb0f\ubb10\ubb11\ubb12\ubb13\ubb14\ubb15\ubb16\ubb17\ubb18\ubb19\ubb1a\ubb1b\ubb1c\ubb1d\ubb1e\ubb1f\ubb20\ubb21\ubb22\ubb23\ubb24\ubb25\ubb26\ubb27\ubb28\ubb29\ubb2a\ubb2b\ubb2c\ubb2d\ubb2e\ubb2f\ubb30\ubb31\ubb32\ubb33\ubb34\ubb35\ubb36\ubb37\ubb38\ubb39\ubb3a\ubb3b\ubb3c\ubb3d\ubb3e\ubb3f\ubb40\ubb41\ubb42\ubb43\ubb44\ubb45\ubb46\ubb47\ubb48\ubb49\ubb4a\ubb4b\ubb4c\ubb4d\ubb4e\ubb4f\ubb50\ubb51\ubb52\ubb53\ubb54\ubb55\ubb56\ubb57\ubb58\ubb59\ubb5a\ubb5b\ubb5c\ubb5d\ubb5e\ubb5f\ubb60\ubb61\ubb62\ubb63\ubb64\ubb65\ubb66\ubb67\ubb68\ubb69\ubb6a\ubb6b\ubb6c\ubb6d\ubb6e\ubb6f\ubb70\ubb71\ubb72\ubb73\ubb74\ubb75\ubb76\ubb77\ubb78\ubb79\ubb7a\ubb7b\ubb7c\ubb7d\ubb7e\ubb7f\ubb80\ubb81\ubb82\ubb83\ubb84\ubb85\ubb86\ubb87\ubb88\ubb89\ubb8a\ubb8b\ubb8c\ubb8d\ubb8e\ubb8f\ubb90\ubb91\ubb92\ubb93\ubb94\ubb95\ubb96\ubb97\ubb98\ubb99\ubb9a\ubb9b\ubb9c\ubb9d\ubb9e\ubb9f\ubba0\ubba1\ubba2\ubba3\ubba4\ubba5\ubba6\ubba7\ubba8\ubba9\ubbaa\ubbab\ubbac\ubbad\ubbae\ubbaf\ubbb0\ubbb1\ubbb2\ubbb3\ubbb4\ubbb5\ubbb6\ubbb7\ubbb8\ubbb9\ubbba\ubbbb\ubbbc\ubbbd\ubbbe\ubbbf\ubbc0\ubbc1\ubbc2\ubbc3\ubbc4\ubbc5\ubbc6\ubbc7\ubbc8\ubbc9\ubbca\ubbcb\ubbcc\ubbcd\ubbce\ubbcf\ubbd0\ubbd1\ubbd2\ubbd3\ubbd4\ubbd5\ubbd6\ubbd7\ubbd8\ubbd9\ubbda\ubbdb\ubbdc\ubbdd\ubbde\ubbdf\ubbe0\ubbe1\ubbe2\ubbe3\ubbe4\ubbe5\ubbe6\ubbe7\ubbe8\ubbe9\ubbea\ubbeb\ubbec\ubbed\ubbee\ubbef\ubbf0\ubbf1\ubbf2\ubbf3\ubbf4\ubbf5\ubbf6\ubbf7\ubbf8\ubbf9\ubbfa\ubbfb\ubbfc\ubbfd\ubbfe\ubbff\ubc00\ubc01\ubc02\ubc03\ubc04\ubc05\ubc06\ubc07\ubc08\ubc09\ubc0a\ubc0b\ubc0c\ubc0d\ubc0e\ubc0f\ubc10\ubc11\ubc12\ubc13\ubc14\ubc15\ubc16\ubc17\ubc18\ubc19\ubc1a\ubc1b\ubc1c\ubc1d\ubc1e\ubc1f\ubc20\ubc21\ubc22\ubc23\ubc24\ubc25\ubc26\ubc27\ubc28\ubc29\ubc2a\ubc2b\ubc2c\ubc2d\ubc2e\ubc2f\ubc30\ubc31\ubc32\ubc33\ubc34\ubc35\ubc36\ubc37\ubc38\ubc39\ubc3a\ubc3b\ubc3c\ubc3d\ubc3e\ubc3f\ubc40\ubc41\ubc42\ubc43\ubc44\ubc45\ubc46\ubc47\ubc48\ubc49\ubc4a\ubc4b\ubc4c\ubc4d\ubc4e\ubc4f\ubc50\ubc51\ubc52\ubc53\ubc54\ubc55\ubc56\ubc57\ubc58\ubc59\ubc5a\ubc5b\ubc5c\ubc5d\ubc5e\ubc5f\ubc60\ubc61\ubc62\ubc63\ubc64\ubc65\ubc66\ubc67\ubc68\ubc69\ubc6a\ubc6b\ubc6c\ubc6d\ubc6e\ubc6f\ubc70\ubc71\ubc72\ubc73\ubc74\ubc75\ubc76\ubc77\ubc78\ubc79\ubc7a\ubc7b\ubc7c\ubc7d\ubc7e\ubc7f\ubc80\ubc81\ubc82\ubc83\ubc84\ubc85\ubc86\ubc87\ubc88\ubc89\ubc8a\ubc8b\ubc8c\ubc8d\ubc8e\ubc8f\ubc90\ubc91\ubc92\ubc93\ubc94\ubc95\ubc96\ubc97\ubc98\ubc99\ubc9a\ubc9b\ubc9c\ubc9d\ubc9e\ubc9f\ubca0\ubca1\ubca2\ubca3\ubca4\ubca5\ubca6\ubca7\ubca8\ubca9\ubcaa\ubcab\ubcac\ubcad\ubcae\ubcaf\ubcb0\ubcb1\ubcb2\ubcb3\ubcb4\ubcb5\ubcb6\ubcb7\ubcb8\ubcb9\ubcba\ubcbb\ubcbc\ubcbd\ubcbe\ubcbf\ubcc0\ubcc1\ubcc2\ubcc3\ubcc4\ubcc5\ubcc6\ubcc7\ubcc8\ubcc9\ubcca\ubccb\ubccc\ubccd\ubcce\ubccf\ubcd0\ubcd1\ubcd2\ubcd3\ubcd4\ubcd5\ubcd6\ubcd7\ubcd8\ubcd9\ubcda\ubcdb\ubcdc\ubcdd\ubcde\ubcdf\ubce0\ubce1\ubce2\ubce3\ubce4\ubce5\ubce6\ubce7\ubce8\ubce9\ubcea\ubceb\ubcec\ubced\ubcee\ubcef\ubcf0\ubcf1\ubcf2\ubcf3\ubcf4\ubcf5\ubcf6\ubcf7\ubcf8\ubcf9\ubcfa\ubcfb\ubcfc\ubcfd\ubcfe\ubcff\ubd00\ubd01\ubd02\ubd03\ubd04\ubd05\ubd06\ubd07\ubd08\ubd09\ubd0a\ubd0b\ubd0c\ubd0d\ubd0e\ubd0f\ubd10\ubd11\ubd12\ubd13\ubd14\ubd15\ubd16\ubd17\ubd18\ubd19\ubd1a\ubd1b\ubd1c\ubd1d\ubd1e\ubd1f\ubd20\ubd21\ubd22\ubd23\ubd24\ubd25\ubd26\ubd27\ubd28\ubd29\ubd2a\ubd2b\ubd2c\ubd2d\ubd2e\ubd2f\ubd30\ubd31\ubd32\ubd33\ubd34\ubd35\ubd36\ubd37\ubd38\ubd39\ubd3a\ubd3b\ubd3c\ubd3d\ubd3e\ubd3f\ubd40\ubd41\ubd42\ubd43\ubd44\ubd45\ubd46\ubd47\ubd48\ubd49\ubd4a\ubd4b\ubd4c\ubd4d\ubd4e\ubd4f\ubd50\ubd51\ubd52\ubd53\ubd54\ubd55\ubd56\ubd57\ubd58\ubd59\ubd5a\ubd5b\ubd5c\ubd5d\ubd5e\ubd5f\ubd60\ubd61\ubd62\ubd63\ubd64\ubd65\ubd66\ubd67\ubd68\ubd69\ubd6a\ubd6b\ubd6c\ubd6d\ubd6e\ubd6f\ubd70\ubd71\ubd72\ubd73\ubd74\ubd75\ubd76\ubd77\ubd78\ubd79\ubd7a\ubd7b\ubd7c\ubd7d\ubd7e\ubd7f\ubd80\ubd81\ubd82\ubd83\ubd84\ubd85\ubd86\ubd87\ubd88\ubd89\ubd8a\ubd8b\ubd8c\ubd8d\ubd8e\ubd8f\ubd90\ubd91\ubd92\ubd93\ubd94\ubd95\ubd96\ubd97\ubd98\ubd99\ubd9a\ubd9b\ubd9c\ubd9d\ubd9e\ubd9f\ubda0\ubda1\ubda2\ubda3\ubda4\ubda5\ubda6\ubda7\ubda8\ubda9\ubdaa\ubdab\ubdac\ubdad\ubdae\ubdaf\ubdb0\ubdb1\ubdb2\ubdb3\ubdb4\ubdb5\ubdb6\ubdb7\ubdb8\ubdb9\ubdba\ubdbb\ubdbc\ubdbd\ubdbe\ubdbf\ubdc0\ubdc1\ubdc2\ubdc3\ubdc4\ubdc5\ubdc6\ubdc7\ubdc8\ubdc9\ubdca\ubdcb\ubdcc\ubdcd\ubdce\ubdcf\ubdd0\ubdd1\ubdd2\ubdd3\ubdd4\ubdd5\ubdd6\ubdd7\ubdd8\ubdd9\ubdda\ubddb\ubddc\ubddd\ubdde\ubddf\ubde0\ubde1\ubde2\ubde3\ubde4\ubde5\ubde6\ubde7\ubde8\ubde9\ubdea\ubdeb\ubdec\ubded\ubdee\ubdef\ubdf0\ubdf1\ubdf2\ubdf3\ubdf4\ubdf5\ubdf6\ubdf7\ubdf8\ubdf9\ubdfa\ubdfb\ubdfc\ubdfd\ubdfe\ubdff\ube00\ube01\ube02\ube03\ube04\ube05\ube06\ube07\ube08\ube09\ube0a\ube0b\ube0c\ube0d\ube0e\ube0f\ube10\ube11\ube12\ube13\ube14\ube15\ube16\ube17\ube18\ube19\ube1a\ube1b\ube1c\ube1d\ube1e\ube1f\ube20\ube21\ube22\ube23\ube24\ube25\ube26\ube27\ube28\ube29\ube2a\ube2b\ube2c\ube2d\ube2e\ube2f\ube30\ube31\ube32\ube33\ube34\ube35\ube36\ube37\ube38\ube39\ube3a\ube3b\ube3c\ube3d\ube3e\ube3f\ube40\ube41\ube42\ube43\ube44\ube45\ube46\ube47\ube48\ube49\ube4a\ube4b\ube4c\ube4d\ube4e\ube4f\ube50\ube51\ube52\ube53\ube54\ube55\ube56\ube57\ube58\ube59\ube5a\ube5b\ube5c\ube5d\ube5e\ube5f\ube60\ube61\ube62\ube63\ube64\ube65\ube66\ube67\ube68\ube69\ube6a\ube6b\ube6c\ube6d\ube6e\ube6f\ube70\ube71\ube72\ube73\ube74\ube75\ube76\ube77\ube78\ube79\ube7a\ube7b\ube7c\ube7d\ube7e\ube7f\ube80\ube81\ube82\ube83\ube84\ube85\ube86\ube87\ube88\ube89\ube8a\ube8b\ube8c\ube8d\ube8e\ube8f\ube90\ube91\ube92\ube93\ube94\ube95\ube96\ube97\ube98\ube99\ube9a\ube9b\ube9c\ube9d\ube9e\ube9f\ubea0\ubea1\ubea2\ubea3\ubea4\ubea5\ubea6\ubea7\ubea8\ubea9\ubeaa\ubeab\ubeac\ubead\ubeae\ubeaf\ubeb0\ubeb1\ubeb2\ubeb3\ubeb4\ubeb5\ubeb6\ubeb7\ubeb8\ubeb9\ubeba\ubebb\ubebc\ubebd\ubebe\ubebf\ubec0\ubec1\ubec2\ubec3\ubec4\ubec5\ubec6\ubec7\ubec8\ubec9\ubeca\ubecb\ubecc\ubecd\ubece\ubecf\ubed0\ubed1\ubed2\ubed3\ubed4\ubed5\ubed6\ubed7\ubed8\ubed9\ubeda\ubedb\ubedc\ubedd\ubede\ubedf\ubee0\ubee1\ubee2\ubee3\ubee4\ubee5\ubee6\ubee7\ubee8\ubee9\ubeea\ubeeb\ubeec\ubeed\ubeee\ubeef\ubef0\ubef1\ubef2\ubef3\ubef4\ubef5\ubef6\ubef7\ubef8\ubef9\ubefa\ubefb\ubefc\ubefd\ubefe\ubeff\ubf00\ubf01\ubf02\ubf03\ubf04\ubf05\ubf06\ubf07\ubf08\ubf09\ubf0a\ubf0b\ubf0c\ubf0d\ubf0e\ubf0f\ubf10\ubf11\ubf12\ubf13\ubf14\ubf15\ubf16\ubf17\ubf18\ubf19\ubf1a\ubf1b\ubf1c\ubf1d\ubf1e\ubf1f\ubf20\ubf21\ubf22\ubf23\ubf24\ubf25\ubf26\ubf27\ubf28\ubf29\ubf2a\ubf2b\ubf2c\ubf2d\ubf2e\ubf2f\ubf30\ubf31\ubf32\ubf33\ubf34\ubf35\ubf36\ubf37\ubf38\ubf39\ubf3a\ubf3b\ubf3c\ubf3d\ubf3e\ubf3f\ubf40\ubf41\ubf42\ubf43\ubf44\ubf45\ubf46\ubf47\ubf48\ubf49\ubf4a\ubf4b\ubf4c\ubf4d\ubf4e\ubf4f\ubf50\ubf51\ubf52\ubf53\ubf54\ubf55\ubf56\ubf57\ubf58\ubf59\ubf5a\ubf5b\ubf5c\ubf5d\ubf5e\ubf5f\ubf60\ubf61\ubf62\ubf63\ubf64\ubf65\ubf66\ubf67\ubf68\ubf69\ubf6a\ubf6b\ubf6c\ubf6d\ubf6e\ubf6f\ubf70\ubf71\ubf72\ubf73\ubf74\ubf75\ubf76\ubf77\ubf78\ubf79\ubf7a\ubf7b\ubf7c\ubf7d\ubf7e\ubf7f\ubf80\ubf81\ubf82\ubf83\ubf84\ubf85\ubf86\ubf87\ubf88\ubf89\ubf8a\ubf8b\ubf8c\ubf8d\ubf8e\ubf8f\ubf90\ubf91\ubf92\ubf93\ubf94\ubf95\ubf96\ubf97\ubf98\ubf99\ubf9a\ubf9b\ubf9c\ubf9d\ubf9e\ubf9f\ubfa0\ubfa1\ubfa2\ubfa3\ubfa4\ubfa5\ubfa6\ubfa7\ubfa8\ubfa9\ubfaa\ubfab\ubfac\ubfad\ubfae\ubfaf\ubfb0\ubfb1\ubfb2\ubfb3\ubfb4\ubfb5\ubfb6\ubfb7\ubfb8\ubfb9\ubfba\ubfbb\ubfbc\ubfbd\ubfbe\ubfbf\ubfc0\ubfc1\ubfc2\ubfc3\ubfc4\ubfc5\ubfc6\ubfc7\ubfc8\ubfc9\ubfca\ubfcb\ubfcc\ubfcd\ubfce\ubfcf\ubfd0\ubfd1\ubfd2\ubfd3\ubfd4\ubfd5\ubfd6\ubfd7\ubfd8\ubfd9\ubfda\ubfdb\ubfdc\ubfdd\ubfde\ubfdf\ubfe0\ubfe1\ubfe2\ubfe3\ubfe4\ubfe5\ubfe6\ubfe7\ubfe8\ubfe9\ubfea\ubfeb\ubfec\ubfed\ubfee\ubfef\ubff0\ubff1\ubff2\ubff3\ubff4\ubff5\ubff6\ubff7\ubff8\ubff9\ubffa\ubffb\ubffc\ubffd\ubffe\ubfff\uc000\uc001\uc002\uc003\uc004\uc005\uc006\uc007\uc008\uc009\uc00a\uc00b\uc00c\uc00d\uc00e\uc00f\uc010\uc011\uc012\uc013\uc014\uc015\uc016\uc017\uc018\uc019\uc01a\uc01b\uc01c\uc01d\uc01e\uc01f\uc020\uc021\uc022\uc023\uc024\uc025\uc026\uc027\uc028\uc029\uc02a\uc02b\uc02c\uc02d\uc02e\uc02f\uc030\uc031\uc032\uc033\uc034\uc035\uc036\uc037\uc038\uc039\uc03a\uc03b\uc03c\uc03d\uc03e\uc03f\uc040\uc041\uc042\uc043\uc044\uc045\uc046\uc047\uc048\uc049\uc04a\uc04b\uc04c\uc04d\uc04e\uc04f\uc050\uc051\uc052\uc053\uc054\uc055\uc056\uc057\uc058\uc059\uc05a\uc05b\uc05c\uc05d\uc05e\uc05f\uc060\uc061\uc062\uc063\uc064\uc065\uc066\uc067\uc068\uc069\uc06a\uc06b\uc06c\uc06d\uc06e\uc06f\uc070\uc071\uc072\uc073\uc074\uc075\uc076\uc077\uc078\uc079\uc07a\uc07b\uc07c\uc07d\uc07e\uc07f\uc080\uc081\uc082\uc083\uc084\uc085\uc086\uc087\uc088\uc089\uc08a\uc08b\uc08c\uc08d\uc08e\uc08f\uc090\uc091\uc092\uc093\uc094\uc095\uc096\uc097\uc098\uc099\uc09a\uc09b\uc09c\uc09d\uc09e\uc09f\uc0a0\uc0a1\uc0a2\uc0a3\uc0a4\uc0a5\uc0a6\uc0a7\uc0a8\uc0a9\uc0aa\uc0ab\uc0ac\uc0ad\uc0ae\uc0af\uc0b0\uc0b1\uc0b2\uc0b3\uc0b4\uc0b5\uc0b6\uc0b7\uc0b8\uc0b9\uc0ba\uc0bb\uc0bc\uc0bd\uc0be\uc0bf\uc0c0\uc0c1\uc0c2\uc0c3\uc0c4\uc0c5\uc0c6\uc0c7\uc0c8\uc0c9\uc0ca\uc0cb\uc0cc\uc0cd\uc0ce\uc0cf\uc0d0\uc0d1\uc0d2\uc0d3\uc0d4\uc0d5\uc0d6\uc0d7\uc0d8\uc0d9\uc0da\uc0db\uc0dc\uc0dd\uc0de\uc0df\uc0e0\uc0e1\uc0e2\uc0e3\uc0e4\uc0e5\uc0e6\uc0e7\uc0e8\uc0e9\uc0ea\uc0eb\uc0ec\uc0ed\uc0ee\uc0ef\uc0f0\uc0f1\uc0f2\uc0f3\uc0f4\uc0f5\uc0f6\uc0f7\uc0f8\uc0f9\uc0fa\uc0fb\uc0fc\uc0fd\uc0fe\uc0ff\uc100\uc101\uc102\uc103\uc104\uc105\uc106\uc107\uc108\uc109\uc10a\uc10b\uc10c\uc10d\uc10e\uc10f\uc110\uc111\uc112\uc113\uc114\uc115\uc116\uc117\uc118\uc119\uc11a\uc11b\uc11c\uc11d\uc11e\uc11f\uc120\uc121\uc122\uc123\uc124\uc125\uc126\uc127\uc128\uc129\uc12a\uc12b\uc12c\uc12d\uc12e\uc12f\uc130\uc131\uc132\uc133\uc134\uc135\uc136\uc137\uc138\uc139\uc13a\uc13b\uc13c\uc13d\uc13e\uc13f\uc140\uc141\uc142\uc143\uc144\uc145\uc146\uc147\uc148\uc149\uc14a\uc14b\uc14c\uc14d\uc14e\uc14f\uc150\uc151\uc152\uc153\uc154\uc155\uc156\uc157\uc158\uc159\uc15a\uc15b\uc15c\uc15d\uc15e\uc15f\uc160\uc161\uc162\uc163\uc164\uc165\uc166\uc167\uc168\uc169\uc16a\uc16b\uc16c\uc16d\uc16e\uc16f\uc170\uc171\uc172\uc173\uc174\uc175\uc176\uc177\uc178\uc179\uc17a\uc17b\uc17c\uc17d\uc17e\uc17f\uc180\uc181\uc182\uc183\uc184\uc185\uc186\uc187\uc188\uc189\uc18a\uc18b\uc18c\uc18d\uc18e\uc18f\uc190\uc191\uc192\uc193\uc194\uc195\uc196\uc197\uc198\uc199\uc19a\uc19b\uc19c\uc19d\uc19e\uc19f\uc1a0\uc1a1\uc1a2\uc1a3\uc1a4\uc1a5\uc1a6\uc1a7\uc1a8\uc1a9\uc1aa\uc1ab\uc1ac\uc1ad\uc1ae\uc1af\uc1b0\uc1b1\uc1b2\uc1b3\uc1b4\uc1b5\uc1b6\uc1b7\uc1b8\uc1b9\uc1ba\uc1bb\uc1bc\uc1bd\uc1be\uc1bf\uc1c0\uc1c1\uc1c2\uc1c3\uc1c4\uc1c5\uc1c6\uc1c7\uc1c8\uc1c9\uc1ca\uc1cb\uc1cc\uc1cd\uc1ce\uc1cf\uc1d0\uc1d1\uc1d2\uc1d3\uc1d4\uc1d5\uc1d6\uc1d7\uc1d8\uc1d9\uc1da\uc1db\uc1dc\uc1dd\uc1de\uc1df\uc1e0\uc1e1\uc1e2\uc1e3\uc1e4\uc1e5\uc1e6\uc1e7\uc1e8\uc1e9\uc1ea\uc1eb\uc1ec\uc1ed\uc1ee\uc1ef\uc1f0\uc1f1\uc1f2\uc1f3\uc1f4\uc1f5\uc1f6\uc1f7\uc1f8\uc1f9\uc1fa\uc1fb\uc1fc\uc1fd\uc1fe\uc1ff\uc200\uc201\uc202\uc203\uc204\uc205\uc206\uc207\uc208\uc209\uc20a\uc20b\uc20c\uc20d\uc20e\uc20f\uc210\uc211\uc212\uc213\uc214\uc215\uc216\uc217\uc218\uc219\uc21a\uc21b\uc21c\uc21d\uc21e\uc21f\uc220\uc221\uc222\uc223\uc224\uc225\uc226\uc227\uc228\uc229\uc22a\uc22b\uc22c\uc22d\uc22e\uc22f\uc230\uc231\uc232\uc233\uc234\uc235\uc236\uc237\uc238\uc239\uc23a\uc23b\uc23c\uc23d\uc23e\uc23f\uc240\uc241\uc242\uc243\uc244\uc245\uc246\uc247\uc248\uc249\uc24a\uc24b\uc24c\uc24d\uc24e\uc24f\uc250\uc251\uc252\uc253\uc254\uc255\uc256\uc257\uc258\uc259\uc25a\uc25b\uc25c\uc25d\uc25e\uc25f\uc260\uc261\uc262\uc263\uc264\uc265\uc266\uc267\uc268\uc269\uc26a\uc26b\uc26c\uc26d\uc26e\uc26f\uc270\uc271\uc272\uc273\uc274\uc275\uc276\uc277\uc278\uc279\uc27a\uc27b\uc27c\uc27d\uc27e\uc27f\uc280\uc281\uc282\uc283\uc284\uc285\uc286\uc287\uc288\uc289\uc28a\uc28b\uc28c\uc28d\uc28e\uc28f\uc290\uc291\uc292\uc293\uc294\uc295\uc296\uc297\uc298\uc299\uc29a\uc29b\uc29c\uc29d\uc29e\uc29f\uc2a0\uc2a1\uc2a2\uc2a3\uc2a4\uc2a5\uc2a6\uc2a7\uc2a8\uc2a9\uc2aa\uc2ab\uc2ac\uc2ad\uc2ae\uc2af\uc2b0\uc2b1\uc2b2\uc2b3\uc2b4\uc2b5\uc2b6\uc2b7\uc2b8\uc2b9\uc2ba\uc2bb\uc2bc\uc2bd\uc2be\uc2bf\uc2c0\uc2c1\uc2c2\uc2c3\uc2c4\uc2c5\uc2c6\uc2c7\uc2c8\uc2c9\uc2ca\uc2cb\uc2cc\uc2cd\uc2ce\uc2cf\uc2d0\uc2d1\uc2d2\uc2d3\uc2d4\uc2d5\uc2d6\uc2d7\uc2d8\uc2d9\uc2da\uc2db\uc2dc\uc2dd\uc2de\uc2df\uc2e0\uc2e1\uc2e2\uc2e3\uc2e4\uc2e5\uc2e6\uc2e7\uc2e8\uc2e9\uc2ea\uc2eb\uc2ec\uc2ed\uc2ee\uc2ef\uc2f0\uc2f1\uc2f2\uc2f3\uc2f4\uc2f5\uc2f6\uc2f7\uc2f8\uc2f9\uc2fa\uc2fb\uc2fc\uc2fd\uc2fe\uc2ff\uc300\uc301\uc302\uc303\uc304\uc305\uc306\uc307\uc308\uc309\uc30a\uc30b\uc30c\uc30d\uc30e\uc30f\uc310\uc311\uc312\uc313\uc314\uc315\uc316\uc317\uc318\uc319\uc31a\uc31b\uc31c\uc31d\uc31e\uc31f\uc320\uc321\uc322\uc323\uc324\uc325\uc326\uc327\uc328\uc329\uc32a\uc32b\uc32c\uc32d\uc32e\uc32f\uc330\uc331\uc332\uc333\uc334\uc335\uc336\uc337\uc338\uc339\uc33a\uc33b\uc33c\uc33d\uc33e\uc33f\uc340\uc341\uc342\uc343\uc344\uc345\uc346\uc347\uc348\uc349\uc34a\uc34b\uc34c\uc34d\uc34e\uc34f\uc350\uc351\uc352\uc353\uc354\uc355\uc356\uc357\uc358\uc359\uc35a\uc35b\uc35c\uc35d\uc35e\uc35f\uc360\uc361\uc362\uc363\uc364\uc365\uc366\uc367\uc368\uc369\uc36a\uc36b\uc36c\uc36d\uc36e\uc36f\uc370\uc371\uc372\uc373\uc374\uc375\uc376\uc377\uc378\uc379\uc37a\uc37b\uc37c\uc37d\uc37e\uc37f\uc380\uc381\uc382\uc383\uc384\uc385\uc386\uc387\uc388\uc389\uc38a\uc38b\uc38c\uc38d\uc38e\uc38f\uc390\uc391\uc392\uc393\uc394\uc395\uc396\uc397\uc398\uc399\uc39a\uc39b\uc39c\uc39d\uc39e\uc39f\uc3a0\uc3a1\uc3a2\uc3a3\uc3a4\uc3a5\uc3a6\uc3a7\uc3a8\uc3a9\uc3aa\uc3ab\uc3ac\uc3ad\uc3ae\uc3af\uc3b0\uc3b1\uc3b2\uc3b3\uc3b4\uc3b5\uc3b6\uc3b7\uc3b8\uc3b9\uc3ba\uc3bb\uc3bc\uc3bd\uc3be\uc3bf\uc3c0\uc3c1\uc3c2\uc3c3\uc3c4\uc3c5\uc3c6\uc3c7\uc3c8\uc3c9\uc3ca\uc3cb\uc3cc\uc3cd\uc3ce\uc3cf\uc3d0\uc3d1\uc3d2\uc3d3\uc3d4\uc3d5\uc3d6\uc3d7\uc3d8\uc3d9\uc3da\uc3db\uc3dc\uc3dd\uc3de\uc3df\uc3e0\uc3e1\uc3e2\uc3e3\uc3e4\uc3e5\uc3e6\uc3e7\uc3e8\uc3e9\uc3ea\uc3eb\uc3ec\uc3ed\uc3ee\uc3ef\uc3f0\uc3f1\uc3f2\uc3f3\uc3f4\uc3f5\uc3f6\uc3f7\uc3f8\uc3f9\uc3fa\uc3fb\uc3fc\uc3fd\uc3fe\uc3ff\uc400\uc401\uc402\uc403\uc404\uc405\uc406\uc407\uc408\uc409\uc40a\uc40b\uc40c\uc40d\uc40e\uc40f\uc410\uc411\uc412\uc413\uc414\uc415\uc416\uc417\uc418\uc419\uc41a\uc41b\uc41c\uc41d\uc41e\uc41f\uc420\uc421\uc422\uc423\uc424\uc425\uc426\uc427\uc428\uc429\uc42a\uc42b\uc42c\uc42d\uc42e\uc42f\uc430\uc431\uc432\uc433\uc434\uc435\uc436\uc437\uc438\uc439\uc43a\uc43b\uc43c\uc43d\uc43e\uc43f\uc440\uc441\uc442\uc443\uc444\uc445\uc446\uc447\uc448\uc449\uc44a\uc44b\uc44c\uc44d\uc44e\uc44f\uc450\uc451\uc452\uc453\uc454\uc455\uc456\uc457\uc458\uc459\uc45a\uc45b\uc45c\uc45d\uc45e\uc45f\uc460\uc461\uc462\uc463\uc464\uc465\uc466\uc467\uc468\uc469\uc46a\uc46b\uc46c\uc46d\uc46e\uc46f\uc470\uc471\uc472\uc473\uc474\uc475\uc476\uc477\uc478\uc479\uc47a\uc47b\uc47c\uc47d\uc47e\uc47f\uc480\uc481\uc482\uc483\uc484\uc485\uc486\uc487\uc488\uc489\uc48a\uc48b\uc48c\uc48d\uc48e\uc48f\uc490\uc491\uc492\uc493\uc494\uc495\uc496\uc497\uc498\uc499\uc49a\uc49b\uc49c\uc49d\uc49e\uc49f\uc4a0\uc4a1\uc4a2\uc4a3\uc4a4\uc4a5\uc4a6\uc4a7\uc4a8\uc4a9\uc4aa\uc4ab\uc4ac\uc4ad\uc4ae\uc4af\uc4b0\uc4b1\uc4b2\uc4b3\uc4b4\uc4b5\uc4b6\uc4b7\uc4b8\uc4b9\uc4ba\uc4bb\uc4bc\uc4bd\uc4be\uc4bf\uc4c0\uc4c1\uc4c2\uc4c3\uc4c4\uc4c5\uc4c6\uc4c7\uc4c8\uc4c9\uc4ca\uc4cb\uc4cc\uc4cd\uc4ce\uc4cf\uc4d0\uc4d1\uc4d2\uc4d3\uc4d4\uc4d5\uc4d6\uc4d7\uc4d8\uc4d9\uc4da\uc4db\uc4dc\uc4dd\uc4de\uc4df\uc4e0\uc4e1\uc4e2\uc4e3\uc4e4\uc4e5\uc4e6\uc4e7\uc4e8\uc4e9\uc4ea\uc4eb\uc4ec\uc4ed\uc4ee\uc4ef\uc4f0\uc4f1\uc4f2\uc4f3\uc4f4\uc4f5\uc4f6\uc4f7\uc4f8\uc4f9\uc4fa\uc4fb\uc4fc\uc4fd\uc4fe\uc4ff\uc500\uc501\uc502\uc503\uc504\uc505\uc506\uc507\uc508\uc509\uc50a\uc50b\uc50c\uc50d\uc50e\uc50f\uc510\uc511\uc512\uc513\uc514\uc515\uc516\uc517\uc518\uc519\uc51a\uc51b\uc51c\uc51d\uc51e\uc51f\uc520\uc521\uc522\uc523\uc524\uc525\uc526\uc527\uc528\uc529\uc52a\uc52b\uc52c\uc52d\uc52e\uc52f\uc530\uc531\uc532\uc533\uc534\uc535\uc536\uc537\uc538\uc539\uc53a\uc53b\uc53c\uc53d\uc53e\uc53f\uc540\uc541\uc542\uc543\uc544\uc545\uc546\uc547\uc548\uc549\uc54a\uc54b\uc54c\uc54d\uc54e\uc54f\uc550\uc551\uc552\uc553\uc554\uc555\uc556\uc557\uc558\uc559\uc55a\uc55b\uc55c\uc55d\uc55e\uc55f\uc560\uc561\uc562\uc563\uc564\uc565\uc566\uc567\uc568\uc569\uc56a\uc56b\uc56c\uc56d\uc56e\uc56f\uc570\uc571\uc572\uc573\uc574\uc575\uc576\uc577\uc578\uc579\uc57a\uc57b\uc57c\uc57d\uc57e\uc57f\uc580\uc581\uc582\uc583\uc584\uc585\uc586\uc587\uc588\uc589\uc58a\uc58b\uc58c\uc58d\uc58e\uc58f\uc590\uc591\uc592\uc593\uc594\uc595\uc596\uc597\uc598\uc599\uc59a\uc59b\uc59c\uc59d\uc59e\uc59f\uc5a0\uc5a1\uc5a2\uc5a3\uc5a4\uc5a5\uc5a6\uc5a7\uc5a8\uc5a9\uc5aa\uc5ab\uc5ac\uc5ad\uc5ae\uc5af\uc5b0\uc5b1\uc5b2\uc5b3\uc5b4\uc5b5\uc5b6\uc5b7\uc5b8\uc5b9\uc5ba\uc5bb\uc5bc\uc5bd\uc5be\uc5bf\uc5c0\uc5c1\uc5c2\uc5c3\uc5c4\uc5c5\uc5c6\uc5c7\uc5c8\uc5c9\uc5ca\uc5cb\uc5cc\uc5cd\uc5ce\uc5cf\uc5d0\uc5d1\uc5d2\uc5d3\uc5d4\uc5d5\uc5d6\uc5d7\uc5d8\uc5d9\uc5da\uc5db\uc5dc\uc5dd\uc5de\uc5df\uc5e0\uc5e1\uc5e2\uc5e3\uc5e4\uc5e5\uc5e6\uc5e7\uc5e8\uc5e9\uc5ea\uc5eb\uc5ec\uc5ed\uc5ee\uc5ef\uc5f0\uc5f1\uc5f2\uc5f3\uc5f4\uc5f5\uc5f6\uc5f7\uc5f8\uc5f9\uc5fa\uc5fb\uc5fc\uc5fd\uc5fe\uc5ff\uc600\uc601\uc602\uc603\uc604\uc605\uc606\uc607\uc608\uc609\uc60a\uc60b\uc60c\uc60d\uc60e\uc60f\uc610\uc611\uc612\uc613\uc614\uc615\uc616\uc617\uc618\uc619\uc61a\uc61b\uc61c\uc61d\uc61e\uc61f\uc620\uc621\uc622\uc623\uc624\uc625\uc626\uc627\uc628\uc629\uc62a\uc62b\uc62c\uc62d\uc62e\uc62f\uc630\uc631\uc632\uc633\uc634\uc635\uc636\uc637\uc638\uc639\uc63a\uc63b\uc63c\uc63d\uc63e\uc63f\uc640\uc641\uc642\uc643\uc644\uc645\uc646\uc647\uc648\uc649\uc64a\uc64b\uc64c\uc64d\uc64e\uc64f\uc650\uc651\uc652\uc653\uc654\uc655\uc656\uc657\uc658\uc659\uc65a\uc65b\uc65c\uc65d\uc65e\uc65f\uc660\uc661\uc662\uc663\uc664\uc665\uc666\uc667\uc668\uc669\uc66a\uc66b\uc66c\uc66d\uc66e\uc66f\uc670\uc671\uc672\uc673\uc674\uc675\uc676\uc677\uc678\uc679\uc67a\uc67b\uc67c\uc67d\uc67e\uc67f\uc680\uc681\uc682\uc683\uc684\uc685\uc686\uc687\uc688\uc689\uc68a\uc68b\uc68c\uc68d\uc68e\uc68f\uc690\uc691\uc692\uc693\uc694\uc695\uc696\uc697\uc698\uc699\uc69a\uc69b\uc69c\uc69d\uc69e\uc69f\uc6a0\uc6a1\uc6a2\uc6a3\uc6a4\uc6a5\uc6a6\uc6a7\uc6a8\uc6a9\uc6aa\uc6ab\uc6ac\uc6ad\uc6ae\uc6af\uc6b0\uc6b1\uc6b2\uc6b3\uc6b4\uc6b5\uc6b6\uc6b7\uc6b8\uc6b9\uc6ba\uc6bb\uc6bc\uc6bd\uc6be\uc6bf\uc6c0\uc6c1\uc6c2\uc6c3\uc6c4\uc6c5\uc6c6\uc6c7\uc6c8\uc6c9\uc6ca\uc6cb\uc6cc\uc6cd\uc6ce\uc6cf\uc6d0\uc6d1\uc6d2\uc6d3\uc6d4\uc6d5\uc6d6\uc6d7\uc6d8\uc6d9\uc6da\uc6db\uc6dc\uc6dd\uc6de\uc6df\uc6e0\uc6e1\uc6e2\uc6e3\uc6e4\uc6e5\uc6e6\uc6e7\uc6e8\uc6e9\uc6ea\uc6eb\uc6ec\uc6ed\uc6ee\uc6ef\uc6f0\uc6f1\uc6f2\uc6f3\uc6f4\uc6f5\uc6f6\uc6f7\uc6f8\uc6f9\uc6fa\uc6fb\uc6fc\uc6fd\uc6fe\uc6ff\uc700\uc701\uc702\uc703\uc704\uc705\uc706\uc707\uc708\uc709\uc70a\uc70b\uc70c\uc70d\uc70e\uc70f\uc710\uc711\uc712\uc713\uc714\uc715\uc716\uc717\uc718\uc719\uc71a\uc71b\uc71c\uc71d\uc71e\uc71f\uc720\uc721\uc722\uc723\uc724\uc725\uc726\uc727\uc728\uc729\uc72a\uc72b\uc72c\uc72d\uc72e\uc72f\uc730\uc731\uc732\uc733\uc734\uc735\uc736\uc737\uc738\uc739\uc73a\uc73b\uc73c\uc73d\uc73e\uc73f\uc740\uc741\uc742\uc743\uc744\uc745\uc746\uc747\uc748\uc749\uc74a\uc74b\uc74c\uc74d\uc74e\uc74f\uc750\uc751\uc752\uc753\uc754\uc755\uc756\uc757\uc758\uc759\uc75a\uc75b\uc75c\uc75d\uc75e\uc75f\uc760\uc761\uc762\uc763\uc764\uc765\uc766\uc767\uc768\uc769\uc76a\uc76b\uc76c\uc76d\uc76e\uc76f\uc770\uc771\uc772\uc773\uc774\uc775\uc776\uc777\uc778\uc779\uc77a\uc77b\uc77c\uc77d\uc77e\uc77f\uc780\uc781\uc782\uc783\uc784\uc785\uc786\uc787\uc788\uc789\uc78a\uc78b\uc78c\uc78d\uc78e\uc78f\uc790\uc791\uc792\uc793\uc794\uc795\uc796\uc797\uc798\uc799\uc79a\uc79b\uc79c\uc79d\uc79e\uc79f\uc7a0\uc7a1\uc7a2\uc7a3\uc7a4\uc7a5\uc7a6\uc7a7\uc7a8\uc7a9\uc7aa\uc7ab\uc7ac\uc7ad\uc7ae\uc7af\uc7b0\uc7b1\uc7b2\uc7b3\uc7b4\uc7b5\uc7b6\uc7b7\uc7b8\uc7b9\uc7ba\uc7bb\uc7bc\uc7bd\uc7be\uc7bf\uc7c0\uc7c1\uc7c2\uc7c3\uc7c4\uc7c5\uc7c6\uc7c7\uc7c8\uc7c9\uc7ca\uc7cb\uc7cc\uc7cd\uc7ce\uc7cf\uc7d0\uc7d1\uc7d2\uc7d3\uc7d4\uc7d5\uc7d6\uc7d7\uc7d8\uc7d9\uc7da\uc7db\uc7dc\uc7dd\uc7de\uc7df\uc7e0\uc7e1\uc7e2\uc7e3\uc7e4\uc7e5\uc7e6\uc7e7\uc7e8\uc7e9\uc7ea\uc7eb\uc7ec\uc7ed\uc7ee\uc7ef\uc7f0\uc7f1\uc7f2\uc7f3\uc7f4\uc7f5\uc7f6\uc7f7\uc7f8\uc7f9\uc7fa\uc7fb\uc7fc\uc7fd\uc7fe\uc7ff\uc800\uc801\uc802\uc803\uc804\uc805\uc806\uc807\uc808\uc809\uc80a\uc80b\uc80c\uc80d\uc80e\uc80f\uc810\uc811\uc812\uc813\uc814\uc815\uc816\uc817\uc818\uc819\uc81a\uc81b\uc81c\uc81d\uc81e\uc81f\uc820\uc821\uc822\uc823\uc824\uc825\uc826\uc827\uc828\uc829\uc82a\uc82b\uc82c\uc82d\uc82e\uc82f\uc830\uc831\uc832\uc833\uc834\uc835\uc836\uc837\uc838\uc839\uc83a\uc83b\uc83c\uc83d\uc83e\uc83f\uc840\uc841\uc842\uc843\uc844\uc845\uc846\uc847\uc848\uc849\uc84a\uc84b\uc84c\uc84d\uc84e\uc84f\uc850\uc851\uc852\uc853\uc854\uc855\uc856\uc857\uc858\uc859\uc85a\uc85b\uc85c\uc85d\uc85e\uc85f\uc860\uc861\uc862\uc863\uc864\uc865\uc866\uc867\uc868\uc869\uc86a\uc86b\uc86c\uc86d\uc86e\uc86f\uc870\uc871\uc872\uc873\uc874\uc875\uc876\uc877\uc878\uc879\uc87a\uc87b\uc87c\uc87d\uc87e\uc87f\uc880\uc881\uc882\uc883\uc884\uc885\uc886\uc887\uc888\uc889\uc88a\uc88b\uc88c\uc88d\uc88e\uc88f\uc890\uc891\uc892\uc893\uc894\uc895\uc896\uc897\uc898\uc899\uc89a\uc89b\uc89c\uc89d\uc89e\uc89f\uc8a0\uc8a1\uc8a2\uc8a3\uc8a4\uc8a5\uc8a6\uc8a7\uc8a8\uc8a9\uc8aa\uc8ab\uc8ac\uc8ad\uc8ae\uc8af\uc8b0\uc8b1\uc8b2\uc8b3\uc8b4\uc8b5\uc8b6\uc8b7\uc8b8\uc8b9\uc8ba\uc8bb\uc8bc\uc8bd\uc8be\uc8bf\uc8c0\uc8c1\uc8c2\uc8c3\uc8c4\uc8c5\uc8c6\uc8c7\uc8c8\uc8c9\uc8ca\uc8cb\uc8cc\uc8cd\uc8ce\uc8cf\uc8d0\uc8d1\uc8d2\uc8d3\uc8d4\uc8d5\uc8d6\uc8d7\uc8d8\uc8d9\uc8da\uc8db\uc8dc\uc8dd\uc8de\uc8df\uc8e0\uc8e1\uc8e2\uc8e3\uc8e4\uc8e5\uc8e6\uc8e7\uc8e8\uc8e9\uc8ea\uc8eb\uc8ec\uc8ed\uc8ee\uc8ef\uc8f0\uc8f1\uc8f2\uc8f3\uc8f4\uc8f5\uc8f6\uc8f7\uc8f8\uc8f9\uc8fa\uc8fb\uc8fc\uc8fd\uc8fe\uc8ff\uc900\uc901\uc902\uc903\uc904\uc905\uc906\uc907\uc908\uc909\uc90a\uc90b\uc90c\uc90d\uc90e\uc90f\uc910\uc911\uc912\uc913\uc914\uc915\uc916\uc917\uc918\uc919\uc91a\uc91b\uc91c\uc91d\uc91e\uc91f\uc920\uc921\uc922\uc923\uc924\uc925\uc926\uc927\uc928\uc929\uc92a\uc92b\uc92c\uc92d\uc92e\uc92f\uc930\uc931\uc932\uc933\uc934\uc935\uc936\uc937\uc938\uc939\uc93a\uc93b\uc93c\uc93d\uc93e\uc93f\uc940\uc941\uc942\uc943\uc944\uc945\uc946\uc947\uc948\uc949\uc94a\uc94b\uc94c\uc94d\uc94e\uc94f\uc950\uc951\uc952\uc953\uc954\uc955\uc956\uc957\uc958\uc959\uc95a\uc95b\uc95c\uc95d\uc95e\uc95f\uc960\uc961\uc962\uc963\uc964\uc965\uc966\uc967\uc968\uc969\uc96a\uc96b\uc96c\uc96d\uc96e\uc96f\uc970\uc971\uc972\uc973\uc974\uc975\uc976\uc977\uc978\uc979\uc97a\uc97b\uc97c\uc97d\uc97e\uc97f\uc980\uc981\uc982\uc983\uc984\uc985\uc986\uc987\uc988\uc989\uc98a\uc98b\uc98c\uc98d\uc98e\uc98f\uc990\uc991\uc992\uc993\uc994\uc995\uc996\uc997\uc998\uc999\uc99a\uc99b\uc99c\uc99d\uc99e\uc99f\uc9a0\uc9a1\uc9a2\uc9a3\uc9a4\uc9a5\uc9a6\uc9a7\uc9a8\uc9a9\uc9aa\uc9ab\uc9ac\uc9ad\uc9ae\uc9af\uc9b0\uc9b1\uc9b2\uc9b3\uc9b4\uc9b5\uc9b6\uc9b7\uc9b8\uc9b9\uc9ba\uc9bb\uc9bc\uc9bd\uc9be\uc9bf\uc9c0\uc9c1\uc9c2\uc9c3\uc9c4\uc9c5\uc9c6\uc9c7\uc9c8\uc9c9\uc9ca\uc9cb\uc9cc\uc9cd\uc9ce\uc9cf\uc9d0\uc9d1\uc9d2\uc9d3\uc9d4\uc9d5\uc9d6\uc9d7\uc9d8\uc9d9\uc9da\uc9db\uc9dc\uc9dd\uc9de\uc9df\uc9e0\uc9e1\uc9e2\uc9e3\uc9e4\uc9e5\uc9e6\uc9e7\uc9e8\uc9e9\uc9ea\uc9eb\uc9ec\uc9ed\uc9ee\uc9ef\uc9f0\uc9f1\uc9f2\uc9f3\uc9f4\uc9f5\uc9f6\uc9f7\uc9f8\uc9f9\uc9fa\uc9fb\uc9fc\uc9fd\uc9fe\uc9ff\uca00\uca01\uca02\uca03\uca04\uca05\uca06\uca07\uca08\uca09\uca0a\uca0b\uca0c\uca0d\uca0e\uca0f\uca10\uca11\uca12\uca13\uca14\uca15\uca16\uca17\uca18\uca19\uca1a\uca1b\uca1c\uca1d\uca1e\uca1f\uca20\uca21\uca22\uca23\uca24\uca25\uca26\uca27\uca28\uca29\uca2a\uca2b\uca2c\uca2d\uca2e\uca2f\uca30\uca31\uca32\uca33\uca34\uca35\uca36\uca37\uca38\uca39\uca3a\uca3b\uca3c\uca3d\uca3e\uca3f\uca40\uca41\uca42\uca43\uca44\uca45\uca46\uca47\uca48\uca49\uca4a\uca4b\uca4c\uca4d\uca4e\uca4f\uca50\uca51\uca52\uca53\uca54\uca55\uca56\uca57\uca58\uca59\uca5a\uca5b\uca5c\uca5d\uca5e\uca5f\uca60\uca61\uca62\uca63\uca64\uca65\uca66\uca67\uca68\uca69\uca6a\uca6b\uca6c\uca6d\uca6e\uca6f\uca70\uca71\uca72\uca73\uca74\uca75\uca76\uca77\uca78\uca79\uca7a\uca7b\uca7c\uca7d\uca7e\uca7f\uca80\uca81\uca82\uca83\uca84\uca85\uca86\uca87\uca88\uca89\uca8a\uca8b\uca8c\uca8d\uca8e\uca8f\uca90\uca91\uca92\uca93\uca94\uca95\uca96\uca97\uca98\uca99\uca9a\uca9b\uca9c\uca9d\uca9e\uca9f\ucaa0\ucaa1\ucaa2\ucaa3\ucaa4\ucaa5\ucaa6\ucaa7\ucaa8\ucaa9\ucaaa\ucaab\ucaac\ucaad\ucaae\ucaaf\ucab0\ucab1\ucab2\ucab3\ucab4\ucab5\ucab6\ucab7\ucab8\ucab9\ucaba\ucabb\ucabc\ucabd\ucabe\ucabf\ucac0\ucac1\ucac2\ucac3\ucac4\ucac5\ucac6\ucac7\ucac8\ucac9\ucaca\ucacb\ucacc\ucacd\ucace\ucacf\ucad0\ucad1\ucad2\ucad3\ucad4\ucad5\ucad6\ucad7\ucad8\ucad9\ucada\ucadb\ucadc\ucadd\ucade\ucadf\ucae0\ucae1\ucae2\ucae3\ucae4\ucae5\ucae6\ucae7\ucae8\ucae9\ucaea\ucaeb\ucaec\ucaed\ucaee\ucaef\ucaf0\ucaf1\ucaf2\ucaf3\ucaf4\ucaf5\ucaf6\ucaf7\ucaf8\ucaf9\ucafa\ucafb\ucafc\ucafd\ucafe\ucaff\ucb00\ucb01\ucb02\ucb03\ucb04\ucb05\ucb06\ucb07\ucb08\ucb09\ucb0a\ucb0b\ucb0c\ucb0d\ucb0e\ucb0f\ucb10\ucb11\ucb12\ucb13\ucb14\ucb15\ucb16\ucb17\ucb18\ucb19\ucb1a\ucb1b\ucb1c\ucb1d\ucb1e\ucb1f\ucb20\ucb21\ucb22\ucb23\ucb24\ucb25\ucb26\ucb27\ucb28\ucb29\ucb2a\ucb2b\ucb2c\ucb2d\ucb2e\ucb2f\ucb30\ucb31\ucb32\ucb33\ucb34\ucb35\ucb36\ucb37\ucb38\ucb39\ucb3a\ucb3b\ucb3c\ucb3d\ucb3e\ucb3f\ucb40\ucb41\ucb42\ucb43\ucb44\ucb45\ucb46\ucb47\ucb48\ucb49\ucb4a\ucb4b\ucb4c\ucb4d\ucb4e\ucb4f\ucb50\ucb51\ucb52\ucb53\ucb54\ucb55\ucb56\ucb57\ucb58\ucb59\ucb5a\ucb5b\ucb5c\ucb5d\ucb5e\ucb5f\ucb60\ucb61\ucb62\ucb63\ucb64\ucb65\ucb66\ucb67\ucb68\ucb69\ucb6a\ucb6b\ucb6c\ucb6d\ucb6e\ucb6f\ucb70\ucb71\ucb72\ucb73\ucb74\ucb75\ucb76\ucb77\ucb78\ucb79\ucb7a\ucb7b\ucb7c\ucb7d\ucb7e\ucb7f\ucb80\ucb81\ucb82\ucb83\ucb84\ucb85\ucb86\ucb87\ucb88\ucb89\ucb8a\ucb8b\ucb8c\ucb8d\ucb8e\ucb8f\ucb90\ucb91\ucb92\ucb93\ucb94\ucb95\ucb96\ucb97\ucb98\ucb99\ucb9a\ucb9b\ucb9c\ucb9d\ucb9e\ucb9f\ucba0\ucba1\ucba2\ucba3\ucba4\ucba5\ucba6\ucba7\ucba8\ucba9\ucbaa\ucbab\ucbac\ucbad\ucbae\ucbaf\ucbb0\ucbb1\ucbb2\ucbb3\ucbb4\ucbb5\ucbb6\ucbb7\ucbb8\ucbb9\ucbba\ucbbb\ucbbc\ucbbd\ucbbe\ucbbf\ucbc0\ucbc1\ucbc2\ucbc3\ucbc4\ucbc5\ucbc6\ucbc7\ucbc8\ucbc9\ucbca\ucbcb\ucbcc\ucbcd\ucbce\ucbcf\ucbd0\ucbd1\ucbd2\ucbd3\ucbd4\ucbd5\ucbd6\ucbd7\ucbd8\ucbd9\ucbda\ucbdb\ucbdc\ucbdd\ucbde\ucbdf\ucbe0\ucbe1\ucbe2\ucbe3\ucbe4\ucbe5\ucbe6\ucbe7\ucbe8\ucbe9\ucbea\ucbeb\ucbec\ucbed\ucbee\ucbef\ucbf0\ucbf1\ucbf2\ucbf3\ucbf4\ucbf5\ucbf6\ucbf7\ucbf8\ucbf9\ucbfa\ucbfb\ucbfc\ucbfd\ucbfe\ucbff\ucc00\ucc01\ucc02\ucc03\ucc04\ucc05\ucc06\ucc07\ucc08\ucc09\ucc0a\ucc0b\ucc0c\ucc0d\ucc0e\ucc0f\ucc10\ucc11\ucc12\ucc13\ucc14\ucc15\ucc16\ucc17\ucc18\ucc19\ucc1a\ucc1b\ucc1c\ucc1d\ucc1e\ucc1f\ucc20\ucc21\ucc22\ucc23\ucc24\ucc25\ucc26\ucc27\ucc28\ucc29\ucc2a\ucc2b\ucc2c\ucc2d\ucc2e\ucc2f\ucc30\ucc31\ucc32\ucc33\ucc34\ucc35\ucc36\ucc37\ucc38\ucc39\ucc3a\ucc3b\ucc3c\ucc3d\ucc3e\ucc3f\ucc40\ucc41\ucc42\ucc43\ucc44\ucc45\ucc46\ucc47\ucc48\ucc49\ucc4a\ucc4b\ucc4c\ucc4d\ucc4e\ucc4f\ucc50\ucc51\ucc52\ucc53\ucc54\ucc55\ucc56\ucc57\ucc58\ucc59\ucc5a\ucc5b\ucc5c\ucc5d\ucc5e\ucc5f\ucc60\ucc61\ucc62\ucc63\ucc64\ucc65\ucc66\ucc67\ucc68\ucc69\ucc6a\ucc6b\ucc6c\ucc6d\ucc6e\ucc6f\ucc70\ucc71\ucc72\ucc73\ucc74\ucc75\ucc76\ucc77\ucc78\ucc79\ucc7a\ucc7b\ucc7c\ucc7d\ucc7e\ucc7f\ucc80\ucc81\ucc82\ucc83\ucc84\ucc85\ucc86\ucc87\ucc88\ucc89\ucc8a\ucc8b\ucc8c\ucc8d\ucc8e\ucc8f\ucc90\ucc91\ucc92\ucc93\ucc94\ucc95\ucc96\ucc97\ucc98\ucc99\ucc9a\ucc9b\ucc9c\ucc9d\ucc9e\ucc9f\ucca0\ucca1\ucca2\ucca3\ucca4\ucca5\ucca6\ucca7\ucca8\ucca9\uccaa\uccab\uccac\uccad\uccae\uccaf\uccb0\uccb1\uccb2\uccb3\uccb4\uccb5\uccb6\uccb7\uccb8\uccb9\uccba\uccbb\uccbc\uccbd\uccbe\uccbf\uccc0\uccc1\uccc2\uccc3\uccc4\uccc5\uccc6\uccc7\uccc8\uccc9\uccca\ucccb\ucccc\ucccd\uccce\ucccf\uccd0\uccd1\uccd2\uccd3\uccd4\uccd5\uccd6\uccd7\uccd8\uccd9\uccda\uccdb\uccdc\uccdd\uccde\uccdf\ucce0\ucce1\ucce2\ucce3\ucce4\ucce5\ucce6\ucce7\ucce8\ucce9\uccea\ucceb\uccec\ucced\uccee\uccef\uccf0\uccf1\uccf2\uccf3\uccf4\uccf5\uccf6\uccf7\uccf8\uccf9\uccfa\uccfb\uccfc\uccfd\uccfe\uccff\ucd00\ucd01\ucd02\ucd03\ucd04\ucd05\ucd06\ucd07\ucd08\ucd09\ucd0a\ucd0b\ucd0c\ucd0d\ucd0e\ucd0f\ucd10\ucd11\ucd12\ucd13\ucd14\ucd15\ucd16\ucd17\ucd18\ucd19\ucd1a\ucd1b\ucd1c\ucd1d\ucd1e\ucd1f\ucd20\ucd21\ucd22\ucd23\ucd24\ucd25\ucd26\ucd27\ucd28\ucd29\ucd2a\ucd2b\ucd2c\ucd2d\ucd2e\ucd2f\ucd30\ucd31\ucd32\ucd33\ucd34\ucd35\ucd36\ucd37\ucd38\ucd39\ucd3a\ucd3b\ucd3c\ucd3d\ucd3e\ucd3f\ucd40\ucd41\ucd42\ucd43\ucd44\ucd45\ucd46\ucd47\ucd48\ucd49\ucd4a\ucd4b\ucd4c\ucd4d\ucd4e\ucd4f\ucd50\ucd51\ucd52\ucd53\ucd54\ucd55\ucd56\ucd57\ucd58\ucd59\ucd5a\ucd5b\ucd5c\ucd5d\ucd5e\ucd5f\ucd60\ucd61\ucd62\ucd63\ucd64\ucd65\ucd66\ucd67\ucd68\ucd69\ucd6a\ucd6b\ucd6c\ucd6d\ucd6e\ucd6f\ucd70\ucd71\ucd72\ucd73\ucd74\ucd75\ucd76\ucd77\ucd78\ucd79\ucd7a\ucd7b\ucd7c\ucd7d\ucd7e\ucd7f\ucd80\ucd81\ucd82\ucd83\ucd84\ucd85\ucd86\ucd87\ucd88\ucd89\ucd8a\ucd8b\ucd8c\ucd8d\ucd8e\ucd8f\ucd90\ucd91\ucd92\ucd93\ucd94\ucd95\ucd96\ucd97\ucd98\ucd99\ucd9a\ucd9b\ucd9c\ucd9d\ucd9e\ucd9f\ucda0\ucda1\ucda2\ucda3\ucda4\ucda5\ucda6\ucda7\ucda8\ucda9\ucdaa\ucdab\ucdac\ucdad\ucdae\ucdaf\ucdb0\ucdb1\ucdb2\ucdb3\ucdb4\ucdb5\ucdb6\ucdb7\ucdb8\ucdb9\ucdba\ucdbb\ucdbc\ucdbd\ucdbe\ucdbf\ucdc0\ucdc1\ucdc2\ucdc3\ucdc4\ucdc5\ucdc6\ucdc7\ucdc8\ucdc9\ucdca\ucdcb\ucdcc\ucdcd\ucdce\ucdcf\ucdd0\ucdd1\ucdd2\ucdd3\ucdd4\ucdd5\ucdd6\ucdd7\ucdd8\ucdd9\ucdda\ucddb\ucddc\ucddd\ucdde\ucddf\ucde0\ucde1\ucde2\ucde3\ucde4\ucde5\ucde6\ucde7\ucde8\ucde9\ucdea\ucdeb\ucdec\ucded\ucdee\ucdef\ucdf0\ucdf1\ucdf2\ucdf3\ucdf4\ucdf5\ucdf6\ucdf7\ucdf8\ucdf9\ucdfa\ucdfb\ucdfc\ucdfd\ucdfe\ucdff\uce00\uce01\uce02\uce03\uce04\uce05\uce06\uce07\uce08\uce09\uce0a\uce0b\uce0c\uce0d\uce0e\uce0f\uce10\uce11\uce12\uce13\uce14\uce15\uce16\uce17\uce18\uce19\uce1a\uce1b\uce1c\uce1d\uce1e\uce1f\uce20\uce21\uce22\uce23\uce24\uce25\uce26\uce27\uce28\uce29\uce2a\uce2b\uce2c\uce2d\uce2e\uce2f\uce30\uce31\uce32\uce33\uce34\uce35\uce36\uce37\uce38\uce39\uce3a\uce3b\uce3c\uce3d\uce3e\uce3f\uce40\uce41\uce42\uce43\uce44\uce45\uce46\uce47\uce48\uce49\uce4a\uce4b\uce4c\uce4d\uce4e\uce4f\uce50\uce51\uce52\uce53\uce54\uce55\uce56\uce57\uce58\uce59\uce5a\uce5b\uce5c\uce5d\uce5e\uce5f\uce60\uce61\uce62\uce63\uce64\uce65\uce66\uce67\uce68\uce69\uce6a\uce6b\uce6c\uce6d\uce6e\uce6f\uce70\uce71\uce72\uce73\uce74\uce75\uce76\uce77\uce78\uce79\uce7a\uce7b\uce7c\uce7d\uce7e\uce7f\uce80\uce81\uce82\uce83\uce84\uce85\uce86\uce87\uce88\uce89\uce8a\uce8b\uce8c\uce8d\uce8e\uce8f\uce90\uce91\uce92\uce93\uce94\uce95\uce96\uce97\uce98\uce99\uce9a\uce9b\uce9c\uce9d\uce9e\uce9f\ucea0\ucea1\ucea2\ucea3\ucea4\ucea5\ucea6\ucea7\ucea8\ucea9\uceaa\uceab\uceac\ucead\uceae\uceaf\uceb0\uceb1\uceb2\uceb3\uceb4\uceb5\uceb6\uceb7\uceb8\uceb9\uceba\ucebb\ucebc\ucebd\ucebe\ucebf\ucec0\ucec1\ucec2\ucec3\ucec4\ucec5\ucec6\ucec7\ucec8\ucec9\uceca\ucecb\ucecc\ucecd\ucece\ucecf\uced0\uced1\uced2\uced3\uced4\uced5\uced6\uced7\uced8\uced9\uceda\ucedb\ucedc\ucedd\ucede\ucedf\ucee0\ucee1\ucee2\ucee3\ucee4\ucee5\ucee6\ucee7\ucee8\ucee9\uceea\uceeb\uceec\uceed\uceee\uceef\ucef0\ucef1\ucef2\ucef3\ucef4\ucef5\ucef6\ucef7\ucef8\ucef9\ucefa\ucefb\ucefc\ucefd\ucefe\uceff\ucf00\ucf01\ucf02\ucf03\ucf04\ucf05\ucf06\ucf07\ucf08\ucf09\ucf0a\ucf0b\ucf0c\ucf0d\ucf0e\ucf0f\ucf10\ucf11\ucf12\ucf13\ucf14\ucf15\ucf16\ucf17\ucf18\ucf19\ucf1a\ucf1b\ucf1c\ucf1d\ucf1e\ucf1f\ucf20\ucf21\ucf22\ucf23\ucf24\ucf25\ucf26\ucf27\ucf28\ucf29\ucf2a\ucf2b\ucf2c\ucf2d\ucf2e\ucf2f\ucf30\ucf31\ucf32\ucf33\ucf34\ucf35\ucf36\ucf37\ucf38\ucf39\ucf3a\ucf3b\ucf3c\ucf3d\ucf3e\ucf3f\ucf40\ucf41\ucf42\ucf43\ucf44\ucf45\ucf46\ucf47\ucf48\ucf49\ucf4a\ucf4b\ucf4c\ucf4d\ucf4e\ucf4f\ucf50\ucf51\ucf52\ucf53\ucf54\ucf55\ucf56\ucf57\ucf58\ucf59\ucf5a\ucf5b\ucf5c\ucf5d\ucf5e\ucf5f\ucf60\ucf61\ucf62\ucf63\ucf64\ucf65\ucf66\ucf67\ucf68\ucf69\ucf6a\ucf6b\ucf6c\ucf6d\ucf6e\ucf6f\ucf70\ucf71\ucf72\ucf73\ucf74\ucf75\ucf76\ucf77\ucf78\ucf79\ucf7a\ucf7b\ucf7c\ucf7d\ucf7e\ucf7f\ucf80\ucf81\ucf82\ucf83\ucf84\ucf85\ucf86\ucf87\ucf88\ucf89\ucf8a\ucf8b\ucf8c\ucf8d\ucf8e\ucf8f\ucf90\ucf91\ucf92\ucf93\ucf94\ucf95\ucf96\ucf97\ucf98\ucf99\ucf9a\ucf9b\ucf9c\ucf9d\ucf9e\ucf9f\ucfa0\ucfa1\ucfa2\ucfa3\ucfa4\ucfa5\ucfa6\ucfa7\ucfa8\ucfa9\ucfaa\ucfab\ucfac\ucfad\ucfae\ucfaf\ucfb0\ucfb1\ucfb2\ucfb3\ucfb4\ucfb5\ucfb6\ucfb7\ucfb8\ucfb9\ucfba\ucfbb\ucfbc\ucfbd\ucfbe\ucfbf\ucfc0\ucfc1\ucfc2\ucfc3\ucfc4\ucfc5\ucfc6\ucfc7\ucfc8\ucfc9\ucfca\ucfcb\ucfcc\ucfcd\ucfce\ucfcf\ucfd0\ucfd1\ucfd2\ucfd3\ucfd4\ucfd5\ucfd6\ucfd7\ucfd8\ucfd9\ucfda\ucfdb\ucfdc\ucfdd\ucfde\ucfdf\ucfe0\ucfe1\ucfe2\ucfe3\ucfe4\ucfe5\ucfe6\ucfe7\ucfe8\ucfe9\ucfea\ucfeb\ucfec\ucfed\ucfee\ucfef\ucff0\ucff1\ucff2\ucff3\ucff4\ucff5\ucff6\ucff7\ucff8\ucff9\ucffa\ucffb\ucffc\ucffd\ucffe\ucfff\ud000\ud001\ud002\ud003\ud004\ud005\ud006\ud007\ud008\ud009\ud00a\ud00b\ud00c\ud00d\ud00e\ud00f\ud010\ud011\ud012\ud013\ud014\ud015\ud016\ud017\ud018\ud019\ud01a\ud01b\ud01c\ud01d\ud01e\ud01f\ud020\ud021\ud022\ud023\ud024\ud025\ud026\ud027\ud028\ud029\ud02a\ud02b\ud02c\ud02d\ud02e\ud02f\ud030\ud031\ud032\ud033\ud034\ud035\ud036\ud037\ud038\ud039\ud03a\ud03b\ud03c\ud03d\ud03e\ud03f\ud040\ud041\ud042\ud043\ud044\ud045\ud046\ud047\ud048\ud049\ud04a\ud04b\ud04c\ud04d\ud04e\ud04f\ud050\ud051\ud052\ud053\ud054\ud055\ud056\ud057\ud058\ud059\ud05a\ud05b\ud05c\ud05d\ud05e\ud05f\ud060\ud061\ud062\ud063\ud064\ud065\ud066\ud067\ud068\ud069\ud06a\ud06b\ud06c\ud06d\ud06e\ud06f\ud070\ud071\ud072\ud073\ud074\ud075\ud076\ud077\ud078\ud079\ud07a\ud07b\ud07c\ud07d\ud07e\ud07f\ud080\ud081\ud082\ud083\ud084\ud085\ud086\ud087\ud088\ud089\ud08a\ud08b\ud08c\ud08d\ud08e\ud08f\ud090\ud091\ud092\ud093\ud094\ud095\ud096\ud097\ud098\ud099\ud09a\ud09b\ud09c\ud09d\ud09e\ud09f\ud0a0\ud0a1\ud0a2\ud0a3\ud0a4\ud0a5\ud0a6\ud0a7\ud0a8\ud0a9\ud0aa\ud0ab\ud0ac\ud0ad\ud0ae\ud0af\ud0b0\ud0b1\ud0b2\ud0b3\ud0b4\ud0b5\ud0b6\ud0b7\ud0b8\ud0b9\ud0ba\ud0bb\ud0bc\ud0bd\ud0be\ud0bf\ud0c0\ud0c1\ud0c2\ud0c3\ud0c4\ud0c5\ud0c6\ud0c7\ud0c8\ud0c9\ud0ca\ud0cb\ud0cc\ud0cd\ud0ce\ud0cf\ud0d0\ud0d1\ud0d2\ud0d3\ud0d4\ud0d5\ud0d6\ud0d7\ud0d8\ud0d9\ud0da\ud0db\ud0dc\ud0dd\ud0de\ud0df\ud0e0\ud0e1\ud0e2\ud0e3\ud0e4\ud0e5\ud0e6\ud0e7\ud0e8\ud0e9\ud0ea\ud0eb\ud0ec\ud0ed\ud0ee\ud0ef\ud0f0\ud0f1\ud0f2\ud0f3\ud0f4\ud0f5\ud0f6\ud0f7\ud0f8\ud0f9\ud0fa\ud0fb\ud0fc\ud0fd\ud0fe\ud0ff\ud100\ud101\ud102\ud103\ud104\ud105\ud106\ud107\ud108\ud109\ud10a\ud10b\ud10c\ud10d\ud10e\ud10f\ud110\ud111\ud112\ud113\ud114\ud115\ud116\ud117\ud118\ud119\ud11a\ud11b\ud11c\ud11d\ud11e\ud11f\ud120\ud121\ud122\ud123\ud124\ud125\ud126\ud127\ud128\ud129\ud12a\ud12b\ud12c\ud12d\ud12e\ud12f\ud130\ud131\ud132\ud133\ud134\ud135\ud136\ud137\ud138\ud139\ud13a\ud13b\ud13c\ud13d\ud13e\ud13f\ud140\ud141\ud142\ud143\ud144\ud145\ud146\ud147\ud148\ud149\ud14a\ud14b\ud14c\ud14d\ud14e\ud14f\ud150\ud151\ud152\ud153\ud154\ud155\ud156\ud157\ud158\ud159\ud15a\ud15b\ud15c\ud15d\ud15e\ud15f\ud160\ud161\ud162\ud163\ud164\ud165\ud166\ud167\ud168\ud169\ud16a\ud16b\ud16c\ud16d\ud16e\ud16f\ud170\ud171\ud172\ud173\ud174\ud175\ud176\ud177\ud178\ud179\ud17a\ud17b\ud17c\ud17d\ud17e\ud17f\ud180\ud181\ud182\ud183\ud184\ud185\ud186\ud187\ud188\ud189\ud18a\ud18b\ud18c\ud18d\ud18e\ud18f\ud190\ud191\ud192\ud193\ud194\ud195\ud196\ud197\ud198\ud199\ud19a\ud19b\ud19c\ud19d\ud19e\ud19f\ud1a0\ud1a1\ud1a2\ud1a3\ud1a4\ud1a5\ud1a6\ud1a7\ud1a8\ud1a9\ud1aa\ud1ab\ud1ac\ud1ad\ud1ae\ud1af\ud1b0\ud1b1\ud1b2\ud1b3\ud1b4\ud1b5\ud1b6\ud1b7\ud1b8\ud1b9\ud1ba\ud1bb\ud1bc\ud1bd\ud1be\ud1bf\ud1c0\ud1c1\ud1c2\ud1c3\ud1c4\ud1c5\ud1c6\ud1c7\ud1c8\ud1c9\ud1ca\ud1cb\ud1cc\ud1cd\ud1ce\ud1cf\ud1d0\ud1d1\ud1d2\ud1d3\ud1d4\ud1d5\ud1d6\ud1d7\ud1d8\ud1d9\ud1da\ud1db\ud1dc\ud1dd\ud1de\ud1df\ud1e0\ud1e1\ud1e2\ud1e3\ud1e4\ud1e5\ud1e6\ud1e7\ud1e8\ud1e9\ud1ea\ud1eb\ud1ec\ud1ed\ud1ee\ud1ef\ud1f0\ud1f1\ud1f2\ud1f3\ud1f4\ud1f5\ud1f6\ud1f7\ud1f8\ud1f9\ud1fa\ud1fb\ud1fc\ud1fd\ud1fe\ud1ff\ud200\ud201\ud202\ud203\ud204\ud205\ud206\ud207\ud208\ud209\ud20a\ud20b\ud20c\ud20d\ud20e\ud20f\ud210\ud211\ud212\ud213\ud214\ud215\ud216\ud217\ud218\ud219\ud21a\ud21b\ud21c\ud21d\ud21e\ud21f\ud220\ud221\ud222\ud223\ud224\ud225\ud226\ud227\ud228\ud229\ud22a\ud22b\ud22c\ud22d\ud22e\ud22f\ud230\ud231\ud232\ud233\ud234\ud235\ud236\ud237\ud238\ud239\ud23a\ud23b\ud23c\ud23d\ud23e\ud23f\ud240\ud241\ud242\ud243\ud244\ud245\ud246\ud247\ud248\ud249\ud24a\ud24b\ud24c\ud24d\ud24e\ud24f\ud250\ud251\ud252\ud253\ud254\ud255\ud256\ud257\ud258\ud259\ud25a\ud25b\ud25c\ud25d\ud25e\ud25f\ud260\ud261\ud262\ud263\ud264\ud265\ud266\ud267\ud268\ud269\ud26a\ud26b\ud26c\ud26d\ud26e\ud26f\ud270\ud271\ud272\ud273\ud274\ud275\ud276\ud277\ud278\ud279\ud27a\ud27b\ud27c\ud27d\ud27e\ud27f\ud280\ud281\ud282\ud283\ud284\ud285\ud286\ud287\ud288\ud289\ud28a\ud28b\ud28c\ud28d\ud28e\ud28f\ud290\ud291\ud292\ud293\ud294\ud295\ud296\ud297\ud298\ud299\ud29a\ud29b\ud29c\ud29d\ud29e\ud29f\ud2a0\ud2a1\ud2a2\ud2a3\ud2a4\ud2a5\ud2a6\ud2a7\ud2a8\ud2a9\ud2aa\ud2ab\ud2ac\ud2ad\ud2ae\ud2af\ud2b0\ud2b1\ud2b2\ud2b3\ud2b4\ud2b5\ud2b6\ud2b7\ud2b8\ud2b9\ud2ba\ud2bb\ud2bc\ud2bd\ud2be\ud2bf\ud2c0\ud2c1\ud2c2\ud2c3\ud2c4\ud2c5\ud2c6\ud2c7\ud2c8\ud2c9\ud2ca\ud2cb\ud2cc\ud2cd\ud2ce\ud2cf\ud2d0\ud2d1\ud2d2\ud2d3\ud2d4\ud2d5\ud2d6\ud2d7\ud2d8\ud2d9\ud2da\ud2db\ud2dc\ud2dd\ud2de\ud2df\ud2e0\ud2e1\ud2e2\ud2e3\ud2e4\ud2e5\ud2e6\ud2e7\ud2e8\ud2e9\ud2ea\ud2eb\ud2ec\ud2ed\ud2ee\ud2ef\ud2f0\ud2f1\ud2f2\ud2f3\ud2f4\ud2f5\ud2f6\ud2f7\ud2f8\ud2f9\ud2fa\ud2fb\ud2fc\ud2fd\ud2fe\ud2ff\ud300\ud301\ud302\ud303\ud304\ud305\ud306\ud307\ud308\ud309\ud30a\ud30b\ud30c\ud30d\ud30e\ud30f\ud310\ud311\ud312\ud313\ud314\ud315\ud316\ud317\ud318\ud319\ud31a\ud31b\ud31c\ud31d\ud31e\ud31f\ud320\ud321\ud322\ud323\ud324\ud325\ud326\ud327\ud328\ud329\ud32a\ud32b\ud32c\ud32d\ud32e\ud32f\ud330\ud331\ud332\ud333\ud334\ud335\ud336\ud337\ud338\ud339\ud33a\ud33b\ud33c\ud33d\ud33e\ud33f\ud340\ud341\ud342\ud343\ud344\ud345\ud346\ud347\ud348\ud349\ud34a\ud34b\ud34c\ud34d\ud34e\ud34f\ud350\ud351\ud352\ud353\ud354\ud355\ud356\ud357\ud358\ud359\ud35a\ud35b\ud35c\ud35d\ud35e\ud35f\ud360\ud361\ud362\ud363\ud364\ud365\ud366\ud367\ud368\ud369\ud36a\ud36b\ud36c\ud36d\ud36e\ud36f\ud370\ud371\ud372\ud373\ud374\ud375\ud376\ud377\ud378\ud379\ud37a\ud37b\ud37c\ud37d\ud37e\ud37f\ud380\ud381\ud382\ud383\ud384\ud385\ud386\ud387\ud388\ud389\ud38a\ud38b\ud38c\ud38d\ud38e\ud38f\ud390\ud391\ud392\ud393\ud394\ud395\ud396\ud397\ud398\ud399\ud39a\ud39b\ud39c\ud39d\ud39e\ud39f\ud3a0\ud3a1\ud3a2\ud3a3\ud3a4\ud3a5\ud3a6\ud3a7\ud3a8\ud3a9\ud3aa\ud3ab\ud3ac\ud3ad\ud3ae\ud3af\ud3b0\ud3b1\ud3b2\ud3b3\ud3b4\ud3b5\ud3b6\ud3b7\ud3b8\ud3b9\ud3ba\ud3bb\ud3bc\ud3bd\ud3be\ud3bf\ud3c0\ud3c1\ud3c2\ud3c3\ud3c4\ud3c5\ud3c6\ud3c7\ud3c8\ud3c9\ud3ca\ud3cb\ud3cc\ud3cd\ud3ce\ud3cf\ud3d0\ud3d1\ud3d2\ud3d3\ud3d4\ud3d5\ud3d6\ud3d7\ud3d8\ud3d9\ud3da\ud3db\ud3dc\ud3dd\ud3de\ud3df\ud3e0\ud3e1\ud3e2\ud3e3\ud3e4\ud3e5\ud3e6\ud3e7\ud3e8\ud3e9\ud3ea\ud3eb\ud3ec\ud3ed\ud3ee\ud3ef\ud3f0\ud3f1\ud3f2\ud3f3\ud3f4\ud3f5\ud3f6\ud3f7\ud3f8\ud3f9\ud3fa\ud3fb\ud3fc\ud3fd\ud3fe\ud3ff\ud400\ud401\ud402\ud403\ud404\ud405\ud406\ud407\ud408\ud409\ud40a\ud40b\ud40c\ud40d\ud40e\ud40f\ud410\ud411\ud412\ud413\ud414\ud415\ud416\ud417\ud418\ud419\ud41a\ud41b\ud41c\ud41d\ud41e\ud41f\ud420\ud421\ud422\ud423\ud424\ud425\ud426\ud427\ud428\ud429\ud42a\ud42b\ud42c\ud42d\ud42e\ud42f\ud430\ud431\ud432\ud433\ud434\ud435\ud436\ud437\ud438\ud439\ud43a\ud43b\ud43c\ud43d\ud43e\ud43f\ud440\ud441\ud442\ud443\ud444\ud445\ud446\ud447\ud448\ud449\ud44a\ud44b\ud44c\ud44d\ud44e\ud44f\ud450\ud451\ud452\ud453\ud454\ud455\ud456\ud457\ud458\ud459\ud45a\ud45b\ud45c\ud45d\ud45e\ud45f\ud460\ud461\ud462\ud463\ud464\ud465\ud466\ud467\ud468\ud469\ud46a\ud46b\ud46c\ud46d\ud46e\ud46f\ud470\ud471\ud472\ud473\ud474\ud475\ud476\ud477\ud478\ud479\ud47a\ud47b\ud47c\ud47d\ud47e\ud47f\ud480\ud481\ud482\ud483\ud484\ud485\ud486\ud487\ud488\ud489\ud48a\ud48b\ud48c\ud48d\ud48e\ud48f\ud490\ud491\ud492\ud493\ud494\ud495\ud496\ud497\ud498\ud499\ud49a\ud49b\ud49c\ud49d\ud49e\ud49f\ud4a0\ud4a1\ud4a2\ud4a3\ud4a4\ud4a5\ud4a6\ud4a7\ud4a8\ud4a9\ud4aa\ud4ab\ud4ac\ud4ad\ud4ae\ud4af\ud4b0\ud4b1\ud4b2\ud4b3\ud4b4\ud4b5\ud4b6\ud4b7\ud4b8\ud4b9\ud4ba\ud4bb\ud4bc\ud4bd\ud4be\ud4bf\ud4c0\ud4c1\ud4c2\ud4c3\ud4c4\ud4c5\ud4c6\ud4c7\ud4c8\ud4c9\ud4ca\ud4cb\ud4cc\ud4cd\ud4ce\ud4cf\ud4d0\ud4d1\ud4d2\ud4d3\ud4d4\ud4d5\ud4d6\ud4d7\ud4d8\ud4d9\ud4da\ud4db\ud4dc\ud4dd\ud4de\ud4df\ud4e0\ud4e1\ud4e2\ud4e3\ud4e4\ud4e5\ud4e6\ud4e7\ud4e8\ud4e9\ud4ea\ud4eb\ud4ec\ud4ed\ud4ee\ud4ef\ud4f0\ud4f1\ud4f2\ud4f3\ud4f4\ud4f5\ud4f6\ud4f7\ud4f8\ud4f9\ud4fa\ud4fb\ud4fc\ud4fd\ud4fe\ud4ff\ud500\ud501\ud502\ud503\ud504\ud505\ud506\ud507\ud508\ud509\ud50a\ud50b\ud50c\ud50d\ud50e\ud50f\ud510\ud511\ud512\ud513\ud514\ud515\ud516\ud517\ud518\ud519\ud51a\ud51b\ud51c\ud51d\ud51e\ud51f\ud520\ud521\ud522\ud523\ud524\ud525\ud526\ud527\ud528\ud529\ud52a\ud52b\ud52c\ud52d\ud52e\ud52f\ud530\ud531\ud532\ud533\ud534\ud535\ud536\ud537\ud538\ud539\ud53a\ud53b\ud53c\ud53d\ud53e\ud53f\ud540\ud541\ud542\ud543\ud544\ud545\ud546\ud547\ud548\ud549\ud54a\ud54b\ud54c\ud54d\ud54e\ud54f\ud550\ud551\ud552\ud553\ud554\ud555\ud556\ud557\ud558\ud559\ud55a\ud55b\ud55c\ud55d\ud55e\ud55f\ud560\ud561\ud562\ud563\ud564\ud565\ud566\ud567\ud568\ud569\ud56a\ud56b\ud56c\ud56d\ud56e\ud56f\ud570\ud571\ud572\ud573\ud574\ud575\ud576\ud577\ud578\ud579\ud57a\ud57b\ud57c\ud57d\ud57e\ud57f\ud580\ud581\ud582\ud583\ud584\ud585\ud586\ud587\ud588\ud589\ud58a\ud58b\ud58c\ud58d\ud58e\ud58f\ud590\ud591\ud592\ud593\ud594\ud595\ud596\ud597\ud598\ud599\ud59a\ud59b\ud59c\ud59d\ud59e\ud59f\ud5a0\ud5a1\ud5a2\ud5a3\ud5a4\ud5a5\ud5a6\ud5a7\ud5a8\ud5a9\ud5aa\ud5ab\ud5ac\ud5ad\ud5ae\ud5af\ud5b0\ud5b1\ud5b2\ud5b3\ud5b4\ud5b5\ud5b6\ud5b7\ud5b8\ud5b9\ud5ba\ud5bb\ud5bc\ud5bd\ud5be\ud5bf\ud5c0\ud5c1\ud5c2\ud5c3\ud5c4\ud5c5\ud5c6\ud5c7\ud5c8\ud5c9\ud5ca\ud5cb\ud5cc\ud5cd\ud5ce\ud5cf\ud5d0\ud5d1\ud5d2\ud5d3\ud5d4\ud5d5\ud5d6\ud5d7\ud5d8\ud5d9\ud5da\ud5db\ud5dc\ud5dd\ud5de\ud5df\ud5e0\ud5e1\ud5e2\ud5e3\ud5e4\ud5e5\ud5e6\ud5e7\ud5e8\ud5e9\ud5ea\ud5eb\ud5ec\ud5ed\ud5ee\ud5ef\ud5f0\ud5f1\ud5f2\ud5f3\ud5f4\ud5f5\ud5f6\ud5f7\ud5f8\ud5f9\ud5fa\ud5fb\ud5fc\ud5fd\ud5fe\ud5ff\ud600\ud601\ud602\ud603\ud604\ud605\ud606\ud607\ud608\ud609\ud60a\ud60b\ud60c\ud60d\ud60e\ud60f\ud610\ud611\ud612\ud613\ud614\ud615\ud616\ud617\ud618\ud619\ud61a\ud61b\ud61c\ud61d\ud61e\ud61f\ud620\ud621\ud622\ud623\ud624\ud625\ud626\ud627\ud628\ud629\ud62a\ud62b\ud62c\ud62d\ud62e\ud62f\ud630\ud631\ud632\ud633\ud634\ud635\ud636\ud637\ud638\ud639\ud63a\ud63b\ud63c\ud63d\ud63e\ud63f\ud640\ud641\ud642\ud643\ud644\ud645\ud646\ud647\ud648\ud649\ud64a\ud64b\ud64c\ud64d\ud64e\ud64f\ud650\ud651\ud652\ud653\ud654\ud655\ud656\ud657\ud658\ud659\ud65a\ud65b\ud65c\ud65d\ud65e\ud65f\ud660\ud661\ud662\ud663\ud664\ud665\ud666\ud667\ud668\ud669\ud66a\ud66b\ud66c\ud66d\ud66e\ud66f\ud670\ud671\ud672\ud673\ud674\ud675\ud676\ud677\ud678\ud679\ud67a\ud67b\ud67c\ud67d\ud67e\ud67f\ud680\ud681\ud682\ud683\ud684\ud685\ud686\ud687\ud688\ud689\ud68a\ud68b\ud68c\ud68d\ud68e\ud68f\ud690\ud691\ud692\ud693\ud694\ud695\ud696\ud697\ud698\ud699\ud69a\ud69b\ud69c\ud69d\ud69e\ud69f\ud6a0\ud6a1\ud6a2\ud6a3\ud6a4\ud6a5\ud6a6\ud6a7\ud6a8\ud6a9\ud6aa\ud6ab\ud6ac\ud6ad\ud6ae\ud6af\ud6b0\ud6b1\ud6b2\ud6b3\ud6b4\ud6b5\ud6b6\ud6b7\ud6b8\ud6b9\ud6ba\ud6bb\ud6bc\ud6bd\ud6be\ud6bf\ud6c0\ud6c1\ud6c2\ud6c3\ud6c4\ud6c5\ud6c6\ud6c7\ud6c8\ud6c9\ud6ca\ud6cb\ud6cc\ud6cd\ud6ce\ud6cf\ud6d0\ud6d1\ud6d2\ud6d3\ud6d4\ud6d5\ud6d6\ud6d7\ud6d8\ud6d9\ud6da\ud6db\ud6dc\ud6dd\ud6de\ud6df\ud6e0\ud6e1\ud6e2\ud6e3\ud6e4\ud6e5\ud6e6\ud6e7\ud6e8\ud6e9\ud6ea\ud6eb\ud6ec\ud6ed\ud6ee\ud6ef\ud6f0\ud6f1\ud6f2\ud6f3\ud6f4\ud6f5\ud6f6\ud6f7\ud6f8\ud6f9\ud6fa\ud6fb\ud6fc\ud6fd\ud6fe\ud6ff\ud700\ud701\ud702\ud703\ud704\ud705\ud706\ud707\ud708\ud709\ud70a\ud70b\ud70c\ud70d\ud70e\ud70f\ud710\ud711\ud712\ud713\ud714\ud715\ud716\ud717\ud718\ud719\ud71a\ud71b\ud71c\ud71d\ud71e\ud71f\ud720\ud721\ud722\ud723\ud724\ud725\ud726\ud727\ud728\ud729\ud72a\ud72b\ud72c\ud72d\ud72e\ud72f\ud730\ud731\ud732\ud733\ud734\ud735\ud736\ud737\ud738\ud739\ud73a\ud73b\ud73c\ud73d\ud73e\ud73f\ud740\ud741\ud742\ud743\ud744\ud745\ud746\ud747\ud748\ud749\ud74a\ud74b\ud74c\ud74d\ud74e\ud74f\ud750\ud751\ud752\ud753\ud754\ud755\ud756\ud757\ud758\ud759\ud75a\ud75b\ud75c\ud75d\ud75e\ud75f\ud760\ud761\ud762\ud763\ud764\ud765\ud766\ud767\ud768\ud769\ud76a\ud76b\ud76c\ud76d\ud76e\ud76f\ud770\ud771\ud772\ud773\ud774\ud775\ud776\ud777\ud778\ud779\ud77a\ud77b\ud77c\ud77d\ud77e\ud77f\ud780\ud781\ud782\ud783\ud784\ud785\ud786\ud787\ud788\ud789\ud78a\ud78b\ud78c\ud78d\ud78e\ud78f\ud790\ud791\ud792\ud793\ud794\ud795\ud796\ud797\ud798\ud799\ud79a\ud79b\ud79c\ud79d\ud79e\ud79f\ud7a0\ud7a1\ud7a2\ud7a3\uf900\uf901\uf902\uf903\uf904\uf905\uf906\uf907\uf908\uf909\uf90a\uf90b\uf90c\uf90d\uf90e\uf90f\uf910\uf911\uf912\uf913\uf914\uf915\uf916\uf917\uf918\uf919\uf91a\uf91b\uf91c\uf91d\uf91e\uf91f\uf920\uf921\uf922\uf923\uf924\uf925\uf926\uf927\uf928\uf929\uf92a\uf92b\uf92c\uf92d\uf92e\uf92f\uf930\uf931\uf932\uf933\uf934\uf935\uf936\uf937\uf938\uf939\uf93a\uf93b\uf93c\uf93d\uf93e\uf93f\uf940\uf941\uf942\uf943\uf944\uf945\uf946\uf947\uf948\uf949\uf94a\uf94b\uf94c\uf94d\uf94e\uf94f\uf950\uf951\uf952\uf953\uf954\uf955\uf956\uf957\uf958\uf959\uf95a\uf95b\uf95c\uf95d\uf95e\uf95f\uf960\uf961\uf962\uf963\uf964\uf965\uf966\uf967\uf968\uf969\uf96a\uf96b\uf96c\uf96d\uf96e\uf96f\uf970\uf971\uf972\uf973\uf974\uf975\uf976\uf977\uf978\uf979\uf97a\uf97b\uf97c\uf97d\uf97e\uf97f\uf980\uf981\uf982\uf983\uf984\uf985\uf986\uf987\uf988\uf989\uf98a\uf98b\uf98c\uf98d\uf98e\uf98f\uf990\uf991\uf992\uf993\uf994\uf995\uf996\uf997\uf998\uf999\uf99a\uf99b\uf99c\uf99d\uf99e\uf99f\uf9a0\uf9a1\uf9a2\uf9a3\uf9a4\uf9a5\uf9a6\uf9a7\uf9a8\uf9a9\uf9aa\uf9ab\uf9ac\uf9ad\uf9ae\uf9af\uf9b0\uf9b1\uf9b2\uf9b3\uf9b4\uf9b5\uf9b6\uf9b7\uf9b8\uf9b9\uf9ba\uf9bb\uf9bc\uf9bd\uf9be\uf9bf\uf9c0\uf9c1\uf9c2\uf9c3\uf9c4\uf9c5\uf9c6\uf9c7\uf9c8\uf9c9\uf9ca\uf9cb\uf9cc\uf9cd\uf9ce\uf9cf\uf9d0\uf9d1\uf9d2\uf9d3\uf9d4\uf9d5\uf9d6\uf9d7\uf9d8\uf9d9\uf9da\uf9db\uf9dc\uf9dd\uf9de\uf9df\uf9e0\uf9e1\uf9e2\uf9e3\uf9e4\uf9e5\uf9e6\uf9e7\uf9e8\uf9e9\uf9ea\uf9eb\uf9ec\uf9ed\uf9ee\uf9ef\uf9f0\uf9f1\uf9f2\uf9f3\uf9f4\uf9f5\uf9f6\uf9f7\uf9f8\uf9f9\uf9fa\uf9fb\uf9fc\uf9fd\uf9fe\uf9ff\ufa00\ufa01\ufa02\ufa03\ufa04\ufa05\ufa06\ufa07\ufa08\ufa09\ufa0a\ufa0b\ufa0c\ufa0d\ufa0e\ufa0f\ufa10\ufa11\ufa12\ufa13\ufa14\ufa15\ufa16\ufa17\ufa18\ufa19\ufa1a\ufa1b\ufa1c\ufa1d\ufa1e\ufa1f\ufa20\ufa21\ufa22\ufa23\ufa24\ufa25\ufa26\ufa27\ufa28\ufa29\ufa2a\ufa2b\ufa2c\ufa2d\ufa30\ufa31\ufa32\ufa33\ufa34\ufa35\ufa36\ufa37\ufa38\ufa39\ufa3a\ufa3b\ufa3c\ufa3d\ufa3e\ufa3f\ufa40\ufa41\ufa42\ufa43\ufa44\ufa45\ufa46\ufa47\ufa48\ufa49\ufa4a\ufa4b\ufa4c\ufa4d\ufa4e\ufa4f\ufa50\ufa51\ufa52\ufa53\ufa54\ufa55\ufa56\ufa57\ufa58\ufa59\ufa5a\ufa5b\ufa5c\ufa5d\ufa5e\ufa5f\ufa60\ufa61\ufa62\ufa63\ufa64\ufa65\ufa66\ufa67\ufa68\ufa69\ufa6a\ufa70\ufa71\ufa72\ufa73\ufa74\ufa75\ufa76\ufa77\ufa78\ufa79\ufa7a\ufa7b\ufa7c\ufa7d\ufa7e\ufa7f\ufa80\ufa81\ufa82\ufa83\ufa84\ufa85\ufa86\ufa87\ufa88\ufa89\ufa8a\ufa8b\ufa8c\ufa8d\ufa8e\ufa8f\ufa90\ufa91\ufa92\ufa93\ufa94\ufa95\ufa96\ufa97\ufa98\ufa99\ufa9a\ufa9b\ufa9c\ufa9d\ufa9e\ufa9f\ufaa0\ufaa1\ufaa2\ufaa3\ufaa4\ufaa5\ufaa6\ufaa7\ufaa8\ufaa9\ufaaa\ufaab\ufaac\ufaad\ufaae\ufaaf\ufab0\ufab1\ufab2\ufab3\ufab4\ufab5\ufab6\ufab7\ufab8\ufab9\ufaba\ufabb\ufabc\ufabd\ufabe\ufabf\ufac0\ufac1\ufac2\ufac3\ufac4\ufac5\ufac6\ufac7\ufac8\ufac9\ufaca\ufacb\ufacc\ufacd\uface\ufacf\ufad0\ufad1\ufad2\ufad3\ufad4\ufad5\ufad6\ufad7\ufad8\ufad9\ufb1d\ufb1f\ufb20\ufb21\ufb22\ufb23\ufb24\ufb25\ufb26\ufb27\ufb28\ufb2a\ufb2b\ufb2c\ufb2d\ufb2e\ufb2f\ufb30\ufb31\ufb32\ufb33\ufb34\ufb35\ufb36\ufb38\ufb39\ufb3a\ufb3b\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46\ufb47\ufb48\ufb49\ufb4a\ufb4b\ufb4c\ufb4d\ufb4e\ufb4f\ufb50\ufb51\ufb52\ufb53\ufb54\ufb55\ufb56\ufb57\ufb58\ufb59\ufb5a\ufb5b\ufb5c\ufb5d\ufb5e\ufb5f\ufb60\ufb61\ufb62\ufb63\ufb64\ufb65\ufb66\ufb67\ufb68\ufb69\ufb6a\ufb6b\ufb6c\ufb6d\ufb6e\ufb6f\ufb70\ufb71\ufb72\ufb73\ufb74\ufb75\ufb76\ufb77\ufb78\ufb79\ufb7a\ufb7b\ufb7c\ufb7d\ufb7e\ufb7f\ufb80\ufb81\ufb82\ufb83\ufb84\ufb85\ufb86\ufb87\ufb88\ufb89\ufb8a\ufb8b\ufb8c\ufb8d\ufb8e\ufb8f\ufb90\ufb91\ufb92\ufb93\ufb94\ufb95\ufb96\ufb97\ufb98\ufb99\ufb9a\ufb9b\ufb9c\ufb9d\ufb9e\ufb9f\ufba0\ufba1\ufba2\ufba3\ufba4\ufba5\ufba6\ufba7\ufba8\ufba9\ufbaa\ufbab\ufbac\ufbad\ufbae\ufbaf\ufbb0\ufbb1\ufbd3\ufbd4\ufbd5\ufbd6\ufbd7\ufbd8\ufbd9\ufbda\ufbdb\ufbdc\ufbdd\ufbde\ufbdf\ufbe0\ufbe1\ufbe2\ufbe3\ufbe4\ufbe5\ufbe6\ufbe7\ufbe8\ufbe9\ufbea\ufbeb\ufbec\ufbed\ufbee\ufbef\ufbf0\ufbf1\ufbf2\ufbf3\ufbf4\ufbf5\ufbf6\ufbf7\ufbf8\ufbf9\ufbfa\ufbfb\ufbfc\ufbfd\ufbfe\ufbff\ufc00\ufc01\ufc02\ufc03\ufc04\ufc05\ufc06\ufc07\ufc08\ufc09\ufc0a\ufc0b\ufc0c\ufc0d\ufc0e\ufc0f\ufc10\ufc11\ufc12\ufc13\ufc14\ufc15\ufc16\ufc17\ufc18\ufc19\ufc1a\ufc1b\ufc1c\ufc1d\ufc1e\ufc1f\ufc20\ufc21\ufc22\ufc23\ufc24\ufc25\ufc26\ufc27\ufc28\ufc29\ufc2a\ufc2b\ufc2c\ufc2d\ufc2e\ufc2f\ufc30\ufc31\ufc32\ufc33\ufc34\ufc35\ufc36\ufc37\ufc38\ufc39\ufc3a\ufc3b\ufc3c\ufc3d\ufc3e\ufc3f\ufc40\ufc41\ufc42\ufc43\ufc44\ufc45\ufc46\ufc47\ufc48\ufc49\ufc4a\ufc4b\ufc4c\ufc4d\ufc4e\ufc4f\ufc50\ufc51\ufc52\ufc53\ufc54\ufc55\ufc56\ufc57\ufc58\ufc59\ufc5a\ufc5b\ufc5c\ufc5d\ufc5e\ufc5f\ufc60\ufc61\ufc62\ufc63\ufc64\ufc65\ufc66\ufc67\ufc68\ufc69\ufc6a\ufc6b\ufc6c\ufc6d\ufc6e\ufc6f\ufc70\ufc71\ufc72\ufc73\ufc74\ufc75\ufc76\ufc77\ufc78\ufc79\ufc7a\ufc7b\ufc7c\ufc7d\ufc7e\ufc7f\ufc80\ufc81\ufc82\ufc83\ufc84\ufc85\ufc86\ufc87\ufc88\ufc89\ufc8a\ufc8b\ufc8c\ufc8d\ufc8e\ufc8f\ufc90\ufc91\ufc92\ufc93\ufc94\ufc95\ufc96\ufc97\ufc98\ufc99\ufc9a\ufc9b\ufc9c\ufc9d\ufc9e\ufc9f\ufca0\ufca1\ufca2\ufca3\ufca4\ufca5\ufca6\ufca7\ufca8\ufca9\ufcaa\ufcab\ufcac\ufcad\ufcae\ufcaf\ufcb0\ufcb1\ufcb2\ufcb3\ufcb4\ufcb5\ufcb6\ufcb7\ufcb8\ufcb9\ufcba\ufcbb\ufcbc\ufcbd\ufcbe\ufcbf\ufcc0\ufcc1\ufcc2\ufcc3\ufcc4\ufcc5\ufcc6\ufcc7\ufcc8\ufcc9\ufcca\ufccb\ufccc\ufccd\ufcce\ufccf\ufcd0\ufcd1\ufcd2\ufcd3\ufcd4\ufcd5\ufcd6\ufcd7\ufcd8\ufcd9\ufcda\ufcdb\ufcdc\ufcdd\ufcde\ufcdf\ufce0\ufce1\ufce2\ufce3\ufce4\ufce5\ufce6\ufce7\ufce8\ufce9\ufcea\ufceb\ufcec\ufced\ufcee\ufcef\ufcf0\ufcf1\ufcf2\ufcf3\ufcf4\ufcf5\ufcf6\ufcf7\ufcf8\ufcf9\ufcfa\ufcfb\ufcfc\ufcfd\ufcfe\ufcff\ufd00\ufd01\ufd02\ufd03\ufd04\ufd05\ufd06\ufd07\ufd08\ufd09\ufd0a\ufd0b\ufd0c\ufd0d\ufd0e\ufd0f\ufd10\ufd11\ufd12\ufd13\ufd14\ufd15\ufd16\ufd17\ufd18\ufd19\ufd1a\ufd1b\ufd1c\ufd1d\ufd1e\ufd1f\ufd20\ufd21\ufd22\ufd23\ufd24\ufd25\ufd26\ufd27\ufd28\ufd29\ufd2a\ufd2b\ufd2c\ufd2d\ufd2e\ufd2f\ufd30\ufd31\ufd32\ufd33\ufd34\ufd35\ufd36\ufd37\ufd38\ufd39\ufd3a\ufd3b\ufd3c\ufd3d\ufd50\ufd51\ufd52\ufd53\ufd54\ufd55\ufd56\ufd57\ufd58\ufd59\ufd5a\ufd5b\ufd5c\ufd5d\ufd5e\ufd5f\ufd60\ufd61\ufd62\ufd63\ufd64\ufd65\ufd66\ufd67\ufd68\ufd69\ufd6a\ufd6b\ufd6c\ufd6d\ufd6e\ufd6f\ufd70\ufd71\ufd72\ufd73\ufd74\ufd75\ufd76\ufd77\ufd78\ufd79\ufd7a\ufd7b\ufd7c\ufd7d\ufd7e\ufd7f\ufd80\ufd81\ufd82\ufd83\ufd84\ufd85\ufd86\ufd87\ufd88\ufd89\ufd8a\ufd8b\ufd8c\ufd8d\ufd8e\ufd8f\ufd92\ufd93\ufd94\ufd95\ufd96\ufd97\ufd98\ufd99\ufd9a\ufd9b\ufd9c\ufd9d\ufd9e\ufd9f\ufda0\ufda1\ufda2\ufda3\ufda4\ufda5\ufda6\ufda7\ufda8\ufda9\ufdaa\ufdab\ufdac\ufdad\ufdae\ufdaf\ufdb0\ufdb1\ufdb2\ufdb3\ufdb4\ufdb5\ufdb6\ufdb7\ufdb8\ufdb9\ufdba\ufdbb\ufdbc\ufdbd\ufdbe\ufdbf\ufdc0\ufdc1\ufdc2\ufdc3\ufdc4\ufdc5\ufdc6\ufdc7\ufdf0\ufdf1\ufdf2\ufdf3\ufdf4\ufdf5\ufdf6\ufdf7\ufdf8\ufdf9\ufdfa\ufdfb\ufe70\ufe71\ufe72\ufe73\ufe74\ufe76\ufe77\ufe78\ufe79\ufe7a\ufe7b\ufe7c\ufe7d\ufe7e\ufe7f\ufe80\ufe81\ufe82\ufe83\ufe84\ufe85\ufe86\ufe87\ufe88\ufe89\ufe8a\ufe8b\ufe8c\ufe8d\ufe8e\ufe8f\ufe90\ufe91\ufe92\ufe93\ufe94\ufe95\ufe96\ufe97\ufe98\ufe99\ufe9a\ufe9b\ufe9c\ufe9d\ufe9e\ufe9f\ufea0\ufea1\ufea2\ufea3\ufea4\ufea5\ufea6\ufea7\ufea8\ufea9\ufeaa\ufeab\ufeac\ufead\ufeae\ufeaf\ufeb0\ufeb1\ufeb2\ufeb3\ufeb4\ufeb5\ufeb6\ufeb7\ufeb8\ufeb9\ufeba\ufebb\ufebc\ufebd\ufebe\ufebf\ufec0\ufec1\ufec2\ufec3\ufec4\ufec5\ufec6\ufec7\ufec8\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1\ufed2\ufed3\ufed4\ufed5\ufed6\ufed7\ufed8\ufed9\ufeda\ufedb\ufedc\ufedd\ufede\ufedf\ufee0\ufee1\ufee2\ufee3\ufee4\ufee5\ufee6\ufee7\ufee8\ufee9\ufeea\ufeeb\ufeec\ufeed\ufeee\ufeef\ufef0\ufef1\ufef2\ufef3\ufef4\ufef5\ufef6\ufef7\ufef8\ufef9\ufefa\ufefb\ufefc\uff66\uff67\uff68\uff69\uff6a\uff6b\uff6c\uff6d\uff6e\uff6f\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78\uff79\uff7a\uff7b\uff7c\uff7d\uff7e\uff7f\uff80\uff81\uff82\uff83\uff84\uff85\uff86\uff87\uff88\uff89\uff8a\uff8b\uff8c\uff8d\uff8e\uff8f\uff90\uff91\uff92\uff93\uff94\uff95\uff96\uff97\uff98\uff99\uff9a\uff9b\uff9c\uff9d\uffa0\uffa1\uffa2\uffa3\uffa4\uffa5\uffa6\uffa7\uffa8\uffa9\uffaa\uffab\uffac\uffad\uffae\uffaf\uffb0\uffb1\uffb2\uffb3\uffb4\uffb5\uffb6\uffb7\uffb8\uffb9\uffba\uffbb\uffbc\uffbd\uffbe\uffc2\uffc3\uffc4\uffc5\uffc6\uffc7\uffca\uffcb\uffcc\uffcd\uffce\uffcf\uffd2\uffd3\uffd4\uffd5\uffd6\uffd7\uffda\uffdb\uffdc'
+
+Lt = u'\u01c5\u01c8\u01cb\u01f2\u1f88\u1f89\u1f8a\u1f8b\u1f8c\u1f8d\u1f8e\u1f8f\u1f98\u1f99\u1f9a\u1f9b\u1f9c\u1f9d\u1f9e\u1f9f\u1fa8\u1fa9\u1faa\u1fab\u1fac\u1fad\u1fae\u1faf\u1fbc\u1fcc\u1ffc'
+
+Lu = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xde\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178\u0179\u017b\u017d\u0181\u0182\u0184\u0186\u0187\u0189\u018a\u018b\u018e\u018f\u0190\u0191\u0193\u0194\u0196\u0197\u0198\u019c\u019d\u019f\u01a0\u01a2\u01a4\u01a6\u01a7\u01a9\u01ac\u01ae\u01af\u01b1\u01b2\u01b3\u01b5\u01b7\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6\u01f7\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a\u023b\u023d\u023e\u0241\u0386\u0388\u0389\u038a\u038c\u038e\u038f\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03d2\u03d3\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9\u03fa\u03fd\u03fe\u03ff\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b\u040c\u040d\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0531\u0532\u0533\u0534\u0535\u0536\u0537\u0538\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054a\u054b\u054c\u054d\u054e\u054f\u0550\u0551\u0552\u0553\u0554\u0555\u0556\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1f08\u1f09\u1f0a\u1f0b\u1f0c\u1f0d\u1f0e\u1f0f\u1f18\u1f19\u1f1a\u1f1b\u1f1c\u1f1d\u1f28\u1f29\u1f2a\u1f2b\u1f2c\u1f2d\u1f2e\u1f2f\u1f38\u1f39\u1f3a\u1f3b\u1f3c\u1f3d\u1f3e\u1f3f\u1f48\u1f49\u1f4a\u1f4b\u1f4c\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68\u1f69\u1f6a\u1f6b\u1f6c\u1f6d\u1f6e\u1f6f\u1fb8\u1fb9\u1fba\u1fbb\u1fc8\u1fc9\u1fca\u1fcb\u1fd8\u1fd9\u1fda\u1fdb\u1fe8\u1fe9\u1fea\u1feb\u1fec\u1ff8\u1ff9\u1ffa\u1ffb\u2102\u2107\u210b\u210c\u210d\u2110\u2111\u2112\u2115\u2119\u211a\u211b\u211c\u211d\u2124\u2126\u2128\u212a\u212b\u212c\u212d\u2130\u2131\u2133\u213e\u213f\u2145\u2c00\u2c01\u2c02\u2c03\u2c04\u2c05\u2c06\u2c07\u2c08\u2c09\u2c0a\u2c0b\u2c0c\u2c0d\u2c0e\u2c0f\u2c10\u2c11\u2c12\u2c13\u2c14\u2c15\u2c16\u2c17\u2c18\u2c19\u2c1a\u2c1b\u2c1c\u2c1d\u2c1e\u2c1f\u2c20\u2c21\u2c22\u2c23\u2c24\u2c25\u2c26\u2c27\u2c28\u2c29\u2c2a\u2c2b\u2c2c\u2c2d\u2c2e\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\uff21\uff22\uff23\uff24\uff25\uff26\uff27\uff28\uff29\uff2a\uff2b\uff2c\uff2d\uff2e\uff2f\uff30\uff31\uff32\uff33\uff34\uff35\uff36\uff37\uff38\uff39\uff3a'
+
+Mc = u'\u0903\u093e\u093f\u0940\u0949\u094a\u094b\u094c\u0982\u0983\u09be\u09bf\u09c0\u09c7\u09c8\u09cb\u09cc\u09d7\u0a03\u0a3e\u0a3f\u0a40\u0a83\u0abe\u0abf\u0ac0\u0ac9\u0acb\u0acc\u0b02\u0b03\u0b3e\u0b40\u0b47\u0b48\u0b4b\u0b4c\u0b57\u0bbe\u0bbf\u0bc1\u0bc2\u0bc6\u0bc7\u0bc8\u0bca\u0bcb\u0bcc\u0bd7\u0c01\u0c02\u0c03\u0c41\u0c42\u0c43\u0c44\u0c82\u0c83\u0cbe\u0cc0\u0cc1\u0cc2\u0cc3\u0cc4\u0cc7\u0cc8\u0cca\u0ccb\u0cd5\u0cd6\u0d02\u0d03\u0d3e\u0d3f\u0d40\u0d46\u0d47\u0d48\u0d4a\u0d4b\u0d4c\u0d57\u0d82\u0d83\u0dcf\u0dd0\u0dd1\u0dd8\u0dd9\u0dda\u0ddb\u0ddc\u0ddd\u0dde\u0ddf\u0df2\u0df3\u0f3e\u0f3f\u0f7f\u102c\u1031\u1038\u1056\u1057\u17b6\u17be\u17bf\u17c0\u17c1\u17c2\u17c3\u17c4\u17c5\u17c7\u17c8\u1923\u1924\u1925\u1926\u1929\u192a\u192b\u1930\u1931\u1933\u1934\u1935\u1936\u1937\u1938\u19b0\u19b1\u19b2\u19b3\u19b4\u19b5\u19b6\u19b7\u19b8\u19b9\u19ba\u19bb\u19bc\u19bd\u19be\u19bf\u19c0\u19c8\u19c9\u1a19\u1a1a\u1a1b\ua802\ua823\ua824\ua827'
+
+Me = u'\u0488\u0489\u06de\u20dd\u20de\u20df\u20e0\u20e2\u20e3\u20e4'
+
+Mn = u'\u0300\u0301\u0302\u0303\u0304\u0305\u0306\u0307\u0308\u0309\u030a\u030b\u030c\u030d\u030e\u030f\u0310\u0311\u0312\u0313\u0314\u0315\u0316\u0317\u0318\u0319\u031a\u031b\u031c\u031d\u031e\u031f\u0320\u0321\u0322\u0323\u0324\u0325\u0326\u0327\u0328\u0329\u032a\u032b\u032c\u032d\u032e\u032f\u0330\u0331\u0332\u0333\u0334\u0335\u0336\u0337\u0338\u0339\u033a\u033b\u033c\u033d\u033e\u033f\u0340\u0341\u0342\u0343\u0344\u0345\u0346\u0347\u0348\u0349\u034a\u034b\u034c\u034d\u034e\u034f\u0350\u0351\u0352\u0353\u0354\u0355\u0356\u0357\u0358\u0359\u035a\u035b\u035c\u035d\u035e\u035f\u0360\u0361\u0362\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036a\u036b\u036c\u036d\u036e\u036f\u0483\u0484\u0485\u0486\u0591\u0592\u0593\u0594\u0595\u0596\u0597\u0598\u0599\u059a\u059b\u059c\u059d\u059e\u059f\u05a0\u05a1\u05a2\u05a3\u05a4\u05a5\u05a6\u05a7\u05a8\u05a9\u05aa\u05ab\u05ac\u05ad\u05ae\u05af\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9\u05bb\u05bc\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610\u0611\u0612\u0613\u0614\u0615\u064b\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u0653\u0654\u0655\u0656\u0657\u0658\u0659\u065a\u065b\u065c\u065d\u065e\u0670\u06d6\u06d7\u06d8\u06d9\u06da\u06db\u06dc\u06df\u06e0\u06e1\u06e2\u06e3\u06e4\u06e7\u06e8\u06ea\u06eb\u06ec\u06ed\u0711\u0730\u0731\u0732\u0733\u0734\u0735\u0736\u0737\u0738\u0739\u073a\u073b\u073c\u073d\u073e\u073f\u0740\u0741\u0742\u0743\u0744\u0745\u0746\u0747\u0748\u0749\u074a\u07a6\u07a7\u07a8\u07a9\u07aa\u07ab\u07ac\u07ad\u07ae\u07af\u07b0\u0901\u0902\u093c\u0941\u0942\u0943\u0944\u0945\u0946\u0947\u0948\u094d\u0951\u0952\u0953\u0954\u0962\u0963\u0981\u09bc\u09c1\u09c2\u09c3\u09c4\u09cd\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b\u0a4c\u0a4d\u0a70\u0a71\u0a81\u0a82\u0abc\u0ac1\u0ac2\u0ac3\u0ac4\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3f\u0b41\u0b42\u0b43\u0b4d\u0b56\u0b82\u0bc0\u0bcd\u0c3e\u0c3f\u0c40\u0c46\u0c47\u0c48\u0c4a\u0c4b\u0c4c\u0c4d\u0c55\u0c56\u0cbc\u0cbf\u0cc6\u0ccc\u0ccd\u0d41\u0d42\u0d43\u0d4d\u0dca\u0dd2\u0dd3\u0dd4\u0dd6\u0e31\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0eb1\u0eb4\u0eb5\u0eb6\u0eb7\u0eb8\u0eb9\u0ebb\u0ebc\u0ec8\u0ec9\u0eca\u0ecb\u0ecc\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71\u0f72\u0f73\u0f74\u0f75\u0f76\u0f77\u0f78\u0f79\u0f7a\u0f7b\u0f7c\u0f7d\u0f7e\u0f80\u0f81\u0f82\u0f83\u0f84\u0f86\u0f87\u0f90\u0f91\u0f92\u0f93\u0f94\u0f95\u0f96\u0f97\u0f99\u0f9a\u0f9b\u0f9c\u0f9d\u0f9e\u0f9f\u0fa0\u0fa1\u0fa2\u0fa3\u0fa4\u0fa5\u0fa6\u0fa7\u0fa8\u0fa9\u0faa\u0fab\u0fac\u0fad\u0fae\u0faf\u0fb0\u0fb1\u0fb2\u0fb3\u0fb4\u0fb5\u0fb6\u0fb7\u0fb8\u0fb9\u0fba\u0fbb\u0fbc\u0fc6\u102d\u102e\u102f\u1030\u1032\u1036\u1037\u1039\u1058\u1059\u135f\u1712\u1713\u1714\u1732\u1733\u1734\u1752\u1753\u1772\u1773\u17b7\u17b8\u17b9\u17ba\u17bb\u17bc\u17bd\u17c6\u17c9\u17ca\u17cb\u17cc\u17cd\u17ce\u17cf\u17d0\u17d1\u17d2\u17d3\u17dd\u180b\u180c\u180d\u18a9\u1920\u1921\u1922\u1927\u1928\u1932\u1939\u193a\u193b\u1a17\u1a18\u1dc0\u1dc1\u1dc2\u1dc3\u20d0\u20d1\u20d2\u20d3\u20d4\u20d5\u20d6\u20d7\u20d8\u20d9\u20da\u20db\u20dc\u20e1\u20e5\u20e6\u20e7\u20e8\u20e9\u20ea\u20eb\u302a\u302b\u302c\u302d\u302e\u302f\u3099\u309a\ua806\ua80b\ua825\ua826\ufb1e\ufe00\ufe01\ufe02\ufe03\ufe04\ufe05\ufe06\ufe07\ufe08\ufe09\ufe0a\ufe0b\ufe0c\ufe0d\ufe0e\ufe0f\ufe20\ufe21\ufe22\ufe23'
+
+Nd = u'0123456789\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u09e6\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u0a66\u0a67\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u0b66\u0b67\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0be6\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\u0f20\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\u0f29\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049\u17e0\u17e1\u17e2\u17e3\u17e4\u17e5\u17e6\u17e7\u17e8\u17e9\u1810\u1811\u1812\u1813\u1814\u1815\u1816\u1817\u1818\u1819\u1946\u1947\u1948\u1949\u194a\u194b\u194c\u194d\u194e\u194f\u19d0\u19d1\u19d2\u19d3\u19d4\u19d5\u19d6\u19d7\u19d8\u19d9\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19'
+
+Nl = u'\u16ee\u16ef\u16f0\u2160\u2161\u2162\u2163\u2164\u2165\u2166\u2167\u2168\u2169\u216a\u216b\u216c\u216d\u216e\u216f\u2170\u2171\u2172\u2173\u2174\u2175\u2176\u2177\u2178\u2179\u217a\u217b\u217c\u217d\u217e\u217f\u2180\u2181\u2182\u2183\u3007\u3021\u3022\u3023\u3024\u3025\u3026\u3027\u3028\u3029\u3038\u3039\u303a'
+
+No = u'\xb2\xb3\xb9\xbc\xbd\xbe\u09f4\u09f5\u09f6\u09f7\u09f8\u09f9\u0bf0\u0bf1\u0bf2\u0f2a\u0f2b\u0f2c\u0f2d\u0f2e\u0f2f\u0f30\u0f31\u0f32\u0f33\u1369\u136a\u136b\u136c\u136d\u136e\u136f\u1370\u1371\u1372\u1373\u1374\u1375\u1376\u1377\u1378\u1379\u137a\u137b\u137c\u17f0\u17f1\u17f2\u17f3\u17f4\u17f5\u17f6\u17f7\u17f8\u17f9\u2070\u2074\u2075\u2076\u2077\u2078\u2079\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2153\u2154\u2155\u2156\u2157\u2158\u2159\u215a\u215b\u215c\u215d\u215e\u215f\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u2474\u2475\u2476\u2477\u2478\u2479\u247a\u247b\u247c\u247d\u247e\u247f\u2480\u2481\u2482\u2483\u2484\u2485\u2486\u2487\u2488\u2489\u248a\u248b\u248c\u248d\u248e\u248f\u2490\u2491\u2492\u2493\u2494\u2495\u2496\u2497\u2498\u2499\u249a\u249b\u24ea\u24eb\u24ec\u24ed\u24ee\u24ef\u24f0\u24f1\u24f2\u24f3\u24f4\u24f5\u24f6\u24f7\u24f8\u24f9\u24fa\u24fb\u24fc\u24fd\u24fe\u24ff\u2776\u2777\u2778\u2779\u277a\u277b\u277c\u277d\u277e\u277f\u2780\u2781\u2782\u2783\u2784\u2785\u2786\u2787\u2788\u2789\u278a\u278b\u278c\u278d\u278e\u278f\u2790\u2791\u2792\u2793\u2cfd\u3192\u3193\u3194\u3195\u3220\u3221\u3222\u3223\u3224\u3225\u3226\u3227\u3228\u3229\u3251\u3252\u3253\u3254\u3255\u3256\u3257\u3258\u3259\u325a\u325b\u325c\u325d\u325e\u325f\u3280\u3281\u3282\u3283\u3284\u3285\u3286\u3287\u3288\u3289\u32b1\u32b2\u32b3\u32b4\u32b5\u32b6\u32b7\u32b8\u32b9\u32ba\u32bb\u32bc\u32bd\u32be\u32bf'
+
+Pc = u'_\u203f\u2040\u2054\ufe33\ufe34\ufe4d\ufe4e\ufe4f\uff3f'
+
+Pd = u'-\u058a\u1806\u2010\u2011\u2012\u2013\u2014\u2015\u2e17\u301c\u3030\u30a0\ufe31\ufe32\ufe58\ufe63\uff0d'
+
+Pe = u')]}\u0f3b\u0f3d\u169c\u2046\u207e\u208e\u232a\u23b5\u2769\u276b\u276d\u276f\u2771\u2773\u2775\u27c6\u27e7\u27e9\u27eb\u2984\u2986\u2988\u298a\u298c\u298e\u2990\u2992\u2994\u2996\u2998\u29d9\u29db\u29fd\u3009\u300b\u300d\u300f\u3011\u3015\u3017\u3019\u301b\u301e\u301f\ufd3f\ufe18\ufe36\ufe38\ufe3a\ufe3c\ufe3e\ufe40\ufe42\ufe44\ufe48\ufe5a\ufe5c\ufe5e\uff09\uff3d\uff5d\uff60\uff63'
+
+Pf = u'\xbb\u2019\u201d\u203a\u2e03\u2e05\u2e0a\u2e0d\u2e1d'
+
+Pi = u'\xab\u2018\u201b\u201c\u201f\u2039\u2e02\u2e04\u2e09\u2e0c\u2e1c'
+
+Po = u'!"#%&\'*,./:;?@\\\xa1\xb7\xbf\u037e\u0387\u055a\u055b\u055c\u055d\u055e\u055f\u0589\u05be\u05c0\u05c3\u05c6\u05f3\u05f4\u060c\u060d\u061b\u061e\u061f\u066a\u066b\u066c\u066d\u06d4\u0700\u0701\u0702\u0703\u0704\u0705\u0706\u0707\u0708\u0709\u070a\u070b\u070c\u070d\u0964\u0965\u0970\u0df4\u0e4f\u0e5a\u0e5b\u0f04\u0f05\u0f06\u0f07\u0f08\u0f09\u0f0a\u0f0b\u0f0c\u0f0d\u0f0e\u0f0f\u0f10\u0f11\u0f12\u0f85\u0fd0\u0fd1\u104a\u104b\u104c\u104d\u104e\u104f\u10fb\u1361\u1362\u1363\u1364\u1365\u1366\u1367\u1368\u166d\u166e\u16eb\u16ec\u16ed\u1735\u1736\u17d4\u17d5\u17d6\u17d8\u17d9\u17da\u1800\u1801\u1802\u1803\u1804\u1805\u1807\u1808\u1809\u180a\u1944\u1945\u19de\u19df\u1a1e\u1a1f\u2016\u2017\u2020\u2021\u2022\u2023\u2024\u2025\u2026\u2027\u2030\u2031\u2032\u2033\u2034\u2035\u2036\u2037\u2038\u203b\u203c\u203d\u203e\u2041\u2042\u2043\u2047\u2048\u2049\u204a\u204b\u204c\u204d\u204e\u204f\u2050\u2051\u2053\u2055\u2056\u2057\u2058\u2059\u205a\u205b\u205c\u205d\u205e\u23b6\u2cf9\u2cfa\u2cfb\u2cfc\u2cfe\u2cff\u2e00\u2e01\u2e06\u2e07\u2e08\u2e0b\u2e0e\u2e0f\u2e10\u2e11\u2e12\u2e13\u2e14\u2e15\u2e16\u3001\u3002\u3003\u303d\u30fb\ufe10\ufe11\ufe12\ufe13\ufe14\ufe15\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49\ufe4a\ufe4b\ufe4c\ufe50\ufe51\ufe52\ufe54\ufe55\ufe56\ufe57\ufe5f\ufe60\ufe61\ufe68\ufe6a\ufe6b\uff01\uff02\uff03\uff05\uff06\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65'
+
+Ps = u'([{\u0f3a\u0f3c\u169b\u201a\u201e\u2045\u207d\u208d\u2329\u23b4\u2768\u276a\u276c\u276e\u2770\u2772\u2774\u27c5\u27e6\u27e8\u27ea\u2983\u2985\u2987\u2989\u298b\u298d\u298f\u2991\u2993\u2995\u2997\u29d8\u29da\u29fc\u3008\u300a\u300c\u300e\u3010\u3014\u3016\u3018\u301a\u301d\ufd3e\ufe17\ufe35\ufe37\ufe39\ufe3b\ufe3d\ufe3f\ufe41\ufe43\ufe47\ufe59\ufe5b\ufe5d\uff08\uff3b\uff5b\uff5f\uff62'
+
+Sc = u'$\xa2\xa3\xa4\xa5\u060b\u09f2\u09f3\u0af1\u0bf9\u0e3f\u17db\u20a0\u20a1\u20a2\u20a3\u20a4\u20a5\u20a6\u20a7\u20a8\u20a9\u20aa\u20ab\u20ac\u20ad\u20ae\u20af\u20b0\u20b1\u20b2\u20b3\u20b4\u20b5\ufdfc\ufe69\uff04\uffe0\uffe1\uffe5\uffe6'
+
+Sk = u'^`\xa8\xaf\xb4\xb8\u02c2\u02c3\u02c4\u02c5\u02d2\u02d3\u02d4\u02d5\u02d6\u02d7\u02d8\u02d9\u02da\u02db\u02dc\u02dd\u02de\u02df\u02e5\u02e6\u02e7\u02e8\u02e9\u02ea\u02eb\u02ec\u02ed\u02ef\u02f0\u02f1\u02f2\u02f3\u02f4\u02f5\u02f6\u02f7\u02f8\u02f9\u02fa\u02fb\u02fc\u02fd\u02fe\u02ff\u0374\u0375\u0384\u0385\u1fbd\u1fbf\u1fc0\u1fc1\u1fcd\u1fce\u1fcf\u1fdd\u1fde\u1fdf\u1fed\u1fee\u1fef\u1ffd\u1ffe\u309b\u309c\ua700\ua701\ua702\ua703\ua704\ua705\ua706\ua707\ua708\ua709\ua70a\ua70b\ua70c\ua70d\ua70e\ua70f\ua710\ua711\ua712\ua713\ua714\ua715\ua716\uff3e\uff40\uffe3'
+
+Sm = u'+<=>|~\xac\xb1\xd7\xf7\u03f6\u2044\u2052\u207a\u207b\u207c\u208a\u208b\u208c\u2140\u2141\u2142\u2143\u2144\u214b\u2190\u2191\u2192\u2193\u2194\u219a\u219b\u21a0\u21a3\u21a6\u21ae\u21ce\u21cf\u21d2\u21d4\u21f4\u21f5\u21f6\u21f7\u21f8\u21f9\u21fa\u21fb\u21fc\u21fd\u21fe\u21ff\u2200\u2201\u2202\u2203\u2204\u2205\u2206\u2207\u2208\u2209\u220a\u220b\u220c\u220d\u220e\u220f\u2210\u2211\u2212\u2213\u2214\u2215\u2216\u2217\u2218\u2219\u221a\u221b\u221c\u221d\u221e\u221f\u2220\u2221\u2222\u2223\u2224\u2225\u2226\u2227\u2228\u2229\u222a\u222b\u222c\u222d\u222e\u222f\u2230\u2231\u2232\u2233\u2234\u2235\u2236\u2237\u2238\u2239\u223a\u223b\u223c\u223d\u223e\u223f\u2240\u2241\u2242\u2243\u2244\u2245\u2246\u2247\u2248\u2249\u224a\u224b\u224c\u224d\u224e\u224f\u2250\u2251\u2252\u2253\u2254\u2255\u2256\u2257\u2258\u2259\u225a\u225b\u225c\u225d\u225e\u225f\u2260\u2261\u2262\u2263\u2264\u2265\u2266\u2267\u2268\u2269\u226a\u226b\u226c\u226d\u226e\u226f\u2270\u2271\u2272\u2273\u2274\u2275\u2276\u2277\u2278\u2279\u227a\u227b\u227c\u227d\u227e\u227f\u2280\u2281\u2282\u2283\u2284\u2285\u2286\u2287\u2288\u2289\u228a\u228b\u228c\u228d\u228e\u228f\u2290\u2291\u2292\u2293\u2294\u2295\u2296\u2297\u2298\u2299\u229a\u229b\u229c\u229d\u229e\u229f\u22a0\u22a1\u22a2\u22a3\u22a4\u22a5\u22a6\u22a7\u22a8\u22a9\u22aa\u22ab\u22ac\u22ad\u22ae\u22af\u22b0\u22b1\u22b2\u22b3\u22b4\u22b5\u22b6\u22b7\u22b8\u22b9\u22ba\u22bb\u22bc\u22bd\u22be\u22bf\u22c0\u22c1\u22c2\u22c3\u22c4\u22c5\u22c6\u22c7\u22c8\u22c9\u22ca\u22cb\u22cc\u22cd\u22ce\u22cf\u22d0\u22d1\u22d2\u22d3\u22d4\u22d5\u22d6\u22d7\u22d8\u22d9\u22da\u22db\u22dc\u22dd\u22de\u22df\u22e0\u22e1\u22e2\u22e3\u22e4\u22e5\u22e6\u22e7\u22e8\u22e9\u22ea\u22eb\u22ec\u22ed\u22ee\u22ef\u22f0\u22f1\u22f2\u22f3\u22f4\u22f5\u22f6\u22f7\u22f8\u22f9\u22fa\u22fb\u22fc\u22fd\u22fe\u22ff\u2308\u2309\u230a\u230b\u2320\u2321\u237c\u239b\u239c\u239d\u239e\u239f\u23a0\u23a1\u23a2\u23a3\u23a4\u23a5\u23a6\u23a7\u23a8\u23a9\u23aa\u23ab\u23ac\u23ad\u23ae\u23af\u23b0\u23b1\u23b2\u23b3\u25b7\u25c1\u25f8\u25f9\u25fa\u25fb\u25fc\u25fd\u25fe\u25ff\u266f\u27c0\u27c1\u27c2\u27c3\u27c4\u27d0\u27d1\u27d2\u27d3\u27d4\u27d5\u27d6\u27d7\u27d8\u27d9\u27da\u27db\u27dc\u27dd\u27de\u27df\u27e0\u27e1\u27e2\u27e3\u27e4\u27e5\u27f0\u27f1\u27f2\u27f3\u27f4\u27f5\u27f6\u27f7\u27f8\u27f9\u27fa\u27fb\u27fc\u27fd\u27fe\u27ff\u2900\u2901\u2902\u2903\u2904\u2905\u2906\u2907\u2908\u2909\u290a\u290b\u290c\u290d\u290e\u290f\u2910\u2911\u2912\u2913\u2914\u2915\u2916\u2917\u2918\u2919\u291a\u291b\u291c\u291d\u291e\u291f\u2920\u2921\u2922\u2923\u2924\u2925\u2926\u2927\u2928\u2929\u292a\u292b\u292c\u292d\u292e\u292f\u2930\u2931\u2932\u2933\u2934\u2935\u2936\u2937\u2938\u2939\u293a\u293b\u293c\u293d\u293e\u293f\u2940\u2941\u2942\u2943\u2944\u2945\u2946\u2947\u2948\u2949\u294a\u294b\u294c\u294d\u294e\u294f\u2950\u2951\u2952\u2953\u2954\u2955\u2956\u2957\u2958\u2959\u295a\u295b\u295c\u295d\u295e\u295f\u2960\u2961\u2962\u2963\u2964\u2965\u2966\u2967\u2968\u2969\u296a\u296b\u296c\u296d\u296e\u296f\u2970\u2971\u2972\u2973\u2974\u2975\u2976\u2977\u2978\u2979\u297a\u297b\u297c\u297d\u297e\u297f\u2980\u2981\u2982\u2999\u299a\u299b\u299c\u299d\u299e\u299f\u29a0\u29a1\u29a2\u29a3\u29a4\u29a5\u29a6\u29a7\u29a8\u29a9\u29aa\u29ab\u29ac\u29ad\u29ae\u29af\u29b0\u29b1\u29b2\u29b3\u29b4\u29b5\u29b6\u29b7\u29b8\u29b9\u29ba\u29bb\u29bc\u29bd\u29be\u29bf\u29c0\u29c1\u29c2\u29c3\u29c4\u29c5\u29c6\u29c7\u29c8\u29c9\u29ca\u29cb\u29cc\u29cd\u29ce\u29cf\u29d0\u29d1\u29d2\u29d3\u29d4\u29d5\u29d6\u29d7\u29dc\u29dd\u29de\u29df\u29e0\u29e1\u29e2\u29e3\u29e4\u29e5\u29e6\u29e7\u29e8\u29e9\u29ea\u29eb\u29ec\u29ed\u29ee\u29ef\u29f0\u29f1\u29f2\u29f3\u29f4\u29f5\u29f6\u29f7\u29f8\u29f9\u29fa\u29fb\u29fe\u29ff\u2a00\u2a01\u2a02\u2a03\u2a04\u2a05\u2a06\u2a07\u2a08\u2a09\u2a0a\u2a0b\u2a0c\u2a0d\u2a0e\u2a0f\u2a10\u2a11\u2a12\u2a13\u2a14\u2a15\u2a16\u2a17\u2a18\u2a19\u2a1a\u2a1b\u2a1c\u2a1d\u2a1e\u2a1f\u2a20\u2a21\u2a22\u2a23\u2a24\u2a25\u2a26\u2a27\u2a28\u2a29\u2a2a\u2a2b\u2a2c\u2a2d\u2a2e\u2a2f\u2a30\u2a31\u2a32\u2a33\u2a34\u2a35\u2a36\u2a37\u2a38\u2a39\u2a3a\u2a3b\u2a3c\u2a3d\u2a3e\u2a3f\u2a40\u2a41\u2a42\u2a43\u2a44\u2a45\u2a46\u2a47\u2a48\u2a49\u2a4a\u2a4b\u2a4c\u2a4d\u2a4e\u2a4f\u2a50\u2a51\u2a52\u2a53\u2a54\u2a55\u2a56\u2a57\u2a58\u2a59\u2a5a\u2a5b\u2a5c\u2a5d\u2a5e\u2a5f\u2a60\u2a61\u2a62\u2a63\u2a64\u2a65\u2a66\u2a67\u2a68\u2a69\u2a6a\u2a6b\u2a6c\u2a6d\u2a6e\u2a6f\u2a70\u2a71\u2a72\u2a73\u2a74\u2a75\u2a76\u2a77\u2a78\u2a79\u2a7a\u2a7b\u2a7c\u2a7d\u2a7e\u2a7f\u2a80\u2a81\u2a82\u2a83\u2a84\u2a85\u2a86\u2a87\u2a88\u2a89\u2a8a\u2a8b\u2a8c\u2a8d\u2a8e\u2a8f\u2a90\u2a91\u2a92\u2a93\u2a94\u2a95\u2a96\u2a97\u2a98\u2a99\u2a9a\u2a9b\u2a9c\u2a9d\u2a9e\u2a9f\u2aa0\u2aa1\u2aa2\u2aa3\u2aa4\u2aa5\u2aa6\u2aa7\u2aa8\u2aa9\u2aaa\u2aab\u2aac\u2aad\u2aae\u2aaf\u2ab0\u2ab1\u2ab2\u2ab3\u2ab4\u2ab5\u2ab6\u2ab7\u2ab8\u2ab9\u2aba\u2abb\u2abc\u2abd\u2abe\u2abf\u2ac0\u2ac1\u2ac2\u2ac3\u2ac4\u2ac5\u2ac6\u2ac7\u2ac8\u2ac9\u2aca\u2acb\u2acc\u2acd\u2ace\u2acf\u2ad0\u2ad1\u2ad2\u2ad3\u2ad4\u2ad5\u2ad6\u2ad7\u2ad8\u2ad9\u2ada\u2adb\u2adc\u2add\u2ade\u2adf\u2ae0\u2ae1\u2ae2\u2ae3\u2ae4\u2ae5\u2ae6\u2ae7\u2ae8\u2ae9\u2aea\u2aeb\u2aec\u2aed\u2aee\u2aef\u2af0\u2af1\u2af2\u2af3\u2af4\u2af5\u2af6\u2af7\u2af8\u2af9\u2afa\u2afb\u2afc\u2afd\u2afe\u2aff\ufb29\ufe62\ufe64\ufe65\ufe66\uff0b\uff1c\uff1d\uff1e\uff5c\uff5e\uffe2\uffe9\uffea\uffeb\uffec'
+
+So = u'\xa6\xa7\xa9\xae\xb0\xb6\u0482\u060e\u060f\u06e9\u06fd\u06fe\u09fa\u0b70\u0bf3\u0bf4\u0bf5\u0bf6\u0bf7\u0bf8\u0bfa\u0f01\u0f02\u0f03\u0f13\u0f14\u0f15\u0f16\u0f17\u0f1a\u0f1b\u0f1c\u0f1d\u0f1e\u0f1f\u0f34\u0f36\u0f38\u0fbe\u0fbf\u0fc0\u0fc1\u0fc2\u0fc3\u0fc4\u0fc5\u0fc7\u0fc8\u0fc9\u0fca\u0fcb\u0fcc\u0fcf\u1360\u1390\u1391\u1392\u1393\u1394\u1395\u1396\u1397\u1398\u1399\u1940\u19e0\u19e1\u19e2\u19e3\u19e4\u19e5\u19e6\u19e7\u19e8\u19e9\u19ea\u19eb\u19ec\u19ed\u19ee\u19ef\u19f0\u19f1\u19f2\u19f3\u19f4\u19f5\u19f6\u19f7\u19f8\u19f9\u19fa\u19fb\u19fc\u19fd\u19fe\u19ff\u2100\u2101\u2103\u2104\u2105\u2106\u2108\u2109\u2114\u2116\u2117\u2118\u211e\u211f\u2120\u2121\u2122\u2123\u2125\u2127\u2129\u212e\u2132\u213a\u213b\u214a\u214c\u2195\u2196\u2197\u2198\u2199\u219c\u219d\u219e\u219f\u21a1\u21a2\u21a4\u21a5\u21a7\u21a8\u21a9\u21aa\u21ab\u21ac\u21ad\u21af\u21b0\u21b1\u21b2\u21b3\u21b4\u21b5\u21b6\u21b7\u21b8\u21b9\u21ba\u21bb\u21bc\u21bd\u21be\u21bf\u21c0\u21c1\u21c2\u21c3\u21c4\u21c5\u21c6\u21c7\u21c8\u21c9\u21ca\u21cb\u21cc\u21cd\u21d0\u21d1\u21d3\u21d5\u21d6\u21d7\u21d8\u21d9\u21da\u21db\u21dc\u21dd\u21de\u21df\u21e0\u21e1\u21e2\u21e3\u21e4\u21e5\u21e6\u21e7\u21e8\u21e9\u21ea\u21eb\u21ec\u21ed\u21ee\u21ef\u21f0\u21f1\u21f2\u21f3\u2300\u2301\u2302\u2303\u2304\u2305\u2306\u2307\u230c\u230d\u230e\u230f\u2310\u2311\u2312\u2313\u2314\u2315\u2316\u2317\u2318\u2319\u231a\u231b\u231c\u231d\u231e\u231f\u2322\u2323\u2324\u2325\u2326\u2327\u2328\u232b\u232c\u232d\u232e\u232f\u2330\u2331\u2332\u2333\u2334\u2335\u2336\u2337\u2338\u2339\u233a\u233b\u233c\u233d\u233e\u233f\u2340\u2341\u2342\u2343\u2344\u2345\u2346\u2347\u2348\u2349\u234a\u234b\u234c\u234d\u234e\u234f\u2350\u2351\u2352\u2353\u2354\u2355\u2356\u2357\u2358\u2359\u235a\u235b\u235c\u235d\u235e\u235f\u2360\u2361\u2362\u2363\u2364\u2365\u2366\u2367\u2368\u2369\u236a\u236b\u236c\u236d\u236e\u236f\u2370\u2371\u2372\u2373\u2374\u2375\u2376\u2377\u2378\u2379\u237a\u237b\u237d\u237e\u237f\u2380\u2381\u2382\u2383\u2384\u2385\u2386\u2387\u2388\u2389\u238a\u238b\u238c\u238d\u238e\u238f\u2390\u2391\u2392\u2393\u2394\u2395\u2396\u2397\u2398\u2399\u239a\u23b7\u23b8\u23b9\u23ba\u23bb\u23bc\u23bd\u23be\u23bf\u23c0\u23c1\u23c2\u23c3\u23c4\u23c5\u23c6\u23c7\u23c8\u23c9\u23ca\u23cb\u23cc\u23cd\u23ce\u23cf\u23d0\u23d1\u23d2\u23d3\u23d4\u23d5\u23d6\u23d7\u23d8\u23d9\u23da\u23db\u2400\u2401\u2402\u2403\u2404\u2405\u2406\u2407\u2408\u2409\u240a\u240b\u240c\u240d\u240e\u240f\u2410\u2411\u2412\u2413\u2414\u2415\u2416\u2417\u2418\u2419\u241a\u241b\u241c\u241d\u241e\u241f\u2420\u2421\u2422\u2423\u2424\u2425\u2426\u2440\u2441\u2442\u2443\u2444\u2445\u2446\u2447\u2448\u2449\u244a\u249c\u249d\u249e\u249f\u24a0\u24a1\u24a2\u24a3\u24a4\u24a5\u24a6\u24a7\u24a8\u24a9\u24aa\u24ab\u24ac\u24ad\u24ae\u24af\u24b0\u24b1\u24b2\u24b3\u24b4\u24b5\u24b6\u24b7\u24b8\u24b9\u24ba\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u2500\u2501\u2502\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250a\u250b\u250c\u250d\u250e\u250f\u2510\u2511\u2512\u2513\u2514\u2515\u2516\u2517\u2518\u2519\u251a\u251b\u251c\u251d\u251e\u251f\u2520\u2521\u2522\u2523\u2524\u2525\u2526\u2527\u2528\u2529\u252a\u252b\u252c\u252d\u252e\u252f\u2530\u2531\u2532\u2533\u2534\u2535\u2536\u2537\u2538\u2539\u253a\u253b\u253c\u253d\u253e\u253f\u2540\u2541\u2542\u2543\u2544\u2545\u2546\u2547\u2548\u2549\u254a\u254b\u254c\u254d\u254e\u254f\u2550\u2551\u2552\u2553\u2554\u2555\u2556\u2557\u2558\u2559\u255a\u255b\u255c\u255d\u255e\u255f\u2560\u2561\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569\u256a\u256b\u256c\u256d\u256e\u256f\u2570\u2571\u2572\u2573\u2574\u2575\u2576\u2577\u2578\u2579\u257a\u257b\u257c\u257d\u257e\u257f\u2580\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2589\u258a\u258b\u258c\u258d\u258e\u258f\u2590\u2591\u2592\u2593\u2594\u2595\u2596\u2597\u2598\u2599\u259a\u259b\u259c\u259d\u259e\u259f\u25a0\u25a1\u25a2\u25a3\u25a4\u25a5\u25a6\u25a7\u25a8\u25a9\u25aa\u25ab\u25ac\u25ad\u25ae\u25af\u25b0\u25b1\u25b2\u25b3\u25b4\u25b5\u25b6\u25b8\u25b9\u25ba\u25bb\u25bc\u25bd\u25be\u25bf\u25c0\u25c2\u25c3\u25c4\u25c5\u25c6\u25c7\u25c8\u25c9\u25ca\u25cb\u25cc\u25cd\u25ce\u25cf\u25d0\u25d1\u25d2\u25d3\u25d4\u25d5\u25d6\u25d7\u25d8\u25d9\u25da\u25db\u25dc\u25dd\u25de\u25df\u25e0\u25e1\u25e2\u25e3\u25e4\u25e5\u25e6\u25e7\u25e8\u25e9\u25ea\u25eb\u25ec\u25ed\u25ee\u25ef\u25f0\u25f1\u25f2\u25f3\u25f4\u25f5\u25f6\u25f7\u2600\u2601\u2602\u2603\u2604\u2605\u2606\u2607\u2608\u2609\u260a\u260b\u260c\u260d\u260e\u260f\u2610\u2611\u2612\u2613\u2614\u2615\u2616\u2617\u2618\u2619\u261a\u261b\u261c\u261d\u261e\u261f\u2620\u2621\u2622\u2623\u2624\u2625\u2626\u2627\u2628\u2629\u262a\u262b\u262c\u262d\u262e\u262f\u2630\u2631\u2632\u2633\u2634\u2635\u2636\u2637\u2638\u2639\u263a\u263b\u263c\u263d\u263e\u263f\u2640\u2641\u2642\u2643\u2644\u2645\u2646\u2647\u2648\u2649\u264a\u264b\u264c\u264d\u264e\u264f\u2650\u2651\u2652\u2653\u2654\u2655\u2656\u2657\u2658\u2659\u265a\u265b\u265c\u265d\u265e\u265f\u2660\u2661\u2662\u2663\u2664\u2665\u2666\u2667\u2668\u2669\u266a\u266b\u266c\u266d\u266e\u2670\u2671\u2672\u2673\u2674\u2675\u2676\u2677\u2678\u2679\u267a\u267b\u267c\u267d\u267e\u267f\u2680\u2681\u2682\u2683\u2684\u2685\u2686\u2687\u2688\u2689\u268a\u268b\u268c\u268d\u268e\u268f\u2690\u2691\u2692\u2693\u2694\u2695\u2696\u2697\u2698\u2699\u269a\u269b\u269c\u26a0\u26a1\u26a2\u26a3\u26a4\u26a5\u26a6\u26a7\u26a8\u26a9\u26aa\u26ab\u26ac\u26ad\u26ae\u26af\u26b0\u26b1\u2701\u2702\u2703\u2704\u2706\u2707\u2708\u2709\u270c\u270d\u270e\u270f\u2710\u2711\u2712\u2713\u2714\u2715\u2716\u2717\u2718\u2719\u271a\u271b\u271c\u271d\u271e\u271f\u2720\u2721\u2722\u2723\u2724\u2725\u2726\u2727\u2729\u272a\u272b\u272c\u272d\u272e\u272f\u2730\u2731\u2732\u2733\u2734\u2735\u2736\u2737\u2738\u2739\u273a\u273b\u273c\u273d\u273e\u273f\u2740\u2741\u2742\u2743\u2744\u2745\u2746\u2747\u2748\u2749\u274a\u274b\u274d\u274f\u2750\u2751\u2752\u2756\u2758\u2759\u275a\u275b\u275c\u275d\u275e\u2761\u2762\u2763\u2764\u2765\u2766\u2767\u2794\u2798\u2799\u279a\u279b\u279c\u279d\u279e\u279f\u27a0\u27a1\u27a2\u27a3\u27a4\u27a5\u27a6\u27a7\u27a8\u27a9\u27aa\u27ab\u27ac\u27ad\u27ae\u27af\u27b1\u27b2\u27b3\u27b4\u27b5\u27b6\u27b7\u27b8\u27b9\u27ba\u27bb\u27bc\u27bd\u27be\u2800\u2801\u2802\u2803\u2804\u2805\u2806\u2807\u2808\u2809\u280a\u280b\u280c\u280d\u280e\u280f\u2810\u2811\u2812\u2813\u2814\u2815\u2816\u2817\u2818\u2819\u281a\u281b\u281c\u281d\u281e\u281f\u2820\u2821\u2822\u2823\u2824\u2825\u2826\u2827\u2828\u2829\u282a\u282b\u282c\u282d\u282e\u282f\u2830\u2831\u2832\u2833\u2834\u2835\u2836\u2837\u2838\u2839\u283a\u283b\u283c\u283d\u283e\u283f\u2840\u2841\u2842\u2843\u2844\u2845\u2846\u2847\u2848\u2849\u284a\u284b\u284c\u284d\u284e\u284f\u2850\u2851\u2852\u2853\u2854\u2855\u2856\u2857\u2858\u2859\u285a\u285b\u285c\u285d\u285e\u285f\u2860\u2861\u2862\u2863\u2864\u2865\u2866\u2867\u2868\u2869\u286a\u286b\u286c\u286d\u286e\u286f\u2870\u2871\u2872\u2873\u2874\u2875\u2876\u2877\u2878\u2879\u287a\u287b\u287c\u287d\u287e\u287f\u2880\u2881\u2882\u2883\u2884\u2885\u2886\u2887\u2888\u2889\u288a\u288b\u288c\u288d\u288e\u288f\u2890\u2891\u2892\u2893\u2894\u2895\u2896\u2897\u2898\u2899\u289a\u289b\u289c\u289d\u289e\u289f\u28a0\u28a1\u28a2\u28a3\u28a4\u28a5\u28a6\u28a7\u28a8\u28a9\u28aa\u28ab\u28ac\u28ad\u28ae\u28af\u28b0\u28b1\u28b2\u28b3\u28b4\u28b5\u28b6\u28b7\u28b8\u28b9\u28ba\u28bb\u28bc\u28bd\u28be\u28bf\u28c0\u28c1\u28c2\u28c3\u28c4\u28c5\u28c6\u28c7\u28c8\u28c9\u28ca\u28cb\u28cc\u28cd\u28ce\u28cf\u28d0\u28d1\u28d2\u28d3\u28d4\u28d5\u28d6\u28d7\u28d8\u28d9\u28da\u28db\u28dc\u28dd\u28de\u28df\u28e0\u28e1\u28e2\u28e3\u28e4\u28e5\u28e6\u28e7\u28e8\u28e9\u28ea\u28eb\u28ec\u28ed\u28ee\u28ef\u28f0\u28f1\u28f2\u28f3\u28f4\u28f5\u28f6\u28f7\u28f8\u28f9\u28fa\u28fb\u28fc\u28fd\u28fe\u28ff\u2b00\u2b01\u2b02\u2b03\u2b04\u2b05\u2b06\u2b07\u2b08\u2b09\u2b0a\u2b0b\u2b0c\u2b0d\u2b0e\u2b0f\u2b10\u2b11\u2b12\u2b13\u2ce5\u2ce6\u2ce7\u2ce8\u2ce9\u2cea\u2e80\u2e81\u2e82\u2e83\u2e84\u2e85\u2e86\u2e87\u2e88\u2e89\u2e8a\u2e8b\u2e8c\u2e8d\u2e8e\u2e8f\u2e90\u2e91\u2e92\u2e93\u2e94\u2e95\u2e96\u2e97\u2e98\u2e99\u2e9b\u2e9c\u2e9d\u2e9e\u2e9f\u2ea0\u2ea1\u2ea2\u2ea3\u2ea4\u2ea5\u2ea6\u2ea7\u2ea8\u2ea9\u2eaa\u2eab\u2eac\u2ead\u2eae\u2eaf\u2eb0\u2eb1\u2eb2\u2eb3\u2eb4\u2eb5\u2eb6\u2eb7\u2eb8\u2eb9\u2eba\u2ebb\u2ebc\u2ebd\u2ebe\u2ebf\u2ec0\u2ec1\u2ec2\u2ec3\u2ec4\u2ec5\u2ec6\u2ec7\u2ec8\u2ec9\u2eca\u2ecb\u2ecc\u2ecd\u2ece\u2ecf\u2ed0\u2ed1\u2ed2\u2ed3\u2ed4\u2ed5\u2ed6\u2ed7\u2ed8\u2ed9\u2eda\u2edb\u2edc\u2edd\u2ede\u2edf\u2ee0\u2ee1\u2ee2\u2ee3\u2ee4\u2ee5\u2ee6\u2ee7\u2ee8\u2ee9\u2eea\u2eeb\u2eec\u2eed\u2eee\u2eef\u2ef0\u2ef1\u2ef2\u2ef3\u2f00\u2f01\u2f02\u2f03\u2f04\u2f05\u2f06\u2f07\u2f08\u2f09\u2f0a\u2f0b\u2f0c\u2f0d\u2f0e\u2f0f\u2f10\u2f11\u2f12\u2f13\u2f14\u2f15\u2f16\u2f17\u2f18\u2f19\u2f1a\u2f1b\u2f1c\u2f1d\u2f1e\u2f1f\u2f20\u2f21\u2f22\u2f23\u2f24\u2f25\u2f26\u2f27\u2f28\u2f29\u2f2a\u2f2b\u2f2c\u2f2d\u2f2e\u2f2f\u2f30\u2f31\u2f32\u2f33\u2f34\u2f35\u2f36\u2f37\u2f38\u2f39\u2f3a\u2f3b\u2f3c\u2f3d\u2f3e\u2f3f\u2f40\u2f41\u2f42\u2f43\u2f44\u2f45\u2f46\u2f47\u2f48\u2f49\u2f4a\u2f4b\u2f4c\u2f4d\u2f4e\u2f4f\u2f50\u2f51\u2f52\u2f53\u2f54\u2f55\u2f56\u2f57\u2f58\u2f59\u2f5a\u2f5b\u2f5c\u2f5d\u2f5e\u2f5f\u2f60\u2f61\u2f62\u2f63\u2f64\u2f65\u2f66\u2f67\u2f68\u2f69\u2f6a\u2f6b\u2f6c\u2f6d\u2f6e\u2f6f\u2f70\u2f71\u2f72\u2f73\u2f74\u2f75\u2f76\u2f77\u2f78\u2f79\u2f7a\u2f7b\u2f7c\u2f7d\u2f7e\u2f7f\u2f80\u2f81\u2f82\u2f83\u2f84\u2f85\u2f86\u2f87\u2f88\u2f89\u2f8a\u2f8b\u2f8c\u2f8d\u2f8e\u2f8f\u2f90\u2f91\u2f92\u2f93\u2f94\u2f95\u2f96\u2f97\u2f98\u2f99\u2f9a\u2f9b\u2f9c\u2f9d\u2f9e\u2f9f\u2fa0\u2fa1\u2fa2\u2fa3\u2fa4\u2fa5\u2fa6\u2fa7\u2fa8\u2fa9\u2faa\u2fab\u2fac\u2fad\u2fae\u2faf\u2fb0\u2fb1\u2fb2\u2fb3\u2fb4\u2fb5\u2fb6\u2fb7\u2fb8\u2fb9\u2fba\u2fbb\u2fbc\u2fbd\u2fbe\u2fbf\u2fc0\u2fc1\u2fc2\u2fc3\u2fc4\u2fc5\u2fc6\u2fc7\u2fc8\u2fc9\u2fca\u2fcb\u2fcc\u2fcd\u2fce\u2fcf\u2fd0\u2fd1\u2fd2\u2fd3\u2fd4\u2fd5\u2ff0\u2ff1\u2ff2\u2ff3\u2ff4\u2ff5\u2ff6\u2ff7\u2ff8\u2ff9\u2ffa\u2ffb\u3004\u3012\u3013\u3020\u3036\u3037\u303e\u303f\u3190\u3191\u3196\u3197\u3198\u3199\u319a\u319b\u319c\u319d\u319e\u319f\u31c0\u31c1\u31c2\u31c3\u31c4\u31c5\u31c6\u31c7\u31c8\u31c9\u31ca\u31cb\u31cc\u31cd\u31ce\u31cf\u3200\u3201\u3202\u3203\u3204\u3205\u3206\u3207\u3208\u3209\u320a\u320b\u320c\u320d\u320e\u320f\u3210\u3211\u3212\u3213\u3214\u3215\u3216\u3217\u3218\u3219\u321a\u321b\u321c\u321d\u321e\u322a\u322b\u322c\u322d\u322e\u322f\u3230\u3231\u3232\u3233\u3234\u3235\u3236\u3237\u3238\u3239\u323a\u323b\u323c\u323d\u323e\u323f\u3240\u3241\u3242\u3243\u3250\u3260\u3261\u3262\u3263\u3264\u3265\u3266\u3267\u3268\u3269\u326a\u326b\u326c\u326d\u326e\u326f\u3270\u3271\u3272\u3273\u3274\u3275\u3276\u3277\u3278\u3279\u327a\u327b\u327c\u327d\u327e\u327f\u328a\u328b\u328c\u328d\u328e\u328f\u3290\u3291\u3292\u3293\u3294\u3295\u3296\u3297\u3298\u3299\u329a\u329b\u329c\u329d\u329e\u329f\u32a0\u32a1\u32a2\u32a3\u32a4\u32a5\u32a6\u32a7\u32a8\u32a9\u32aa\u32ab\u32ac\u32ad\u32ae\u32af\u32b0\u32c0\u32c1\u32c2\u32c3\u32c4\u32c5\u32c6\u32c7\u32c8\u32c9\u32ca\u32cb\u32cc\u32cd\u32ce\u32cf\u32d0\u32d1\u32d2\u32d3\u32d4\u32d5\u32d6\u32d7\u32d8\u32d9\u32da\u32db\u32dc\u32dd\u32de\u32df\u32e0\u32e1\u32e2\u32e3\u32e4\u32e5\u32e6\u32e7\u32e8\u32e9\u32ea\u32eb\u32ec\u32ed\u32ee\u32ef\u32f0\u32f1\u32f2\u32f3\u32f4\u32f5\u32f6\u32f7\u32f8\u32f9\u32fa\u32fb\u32fc\u32fd\u32fe\u3300\u3301\u3302\u3303\u3304\u3305\u3306\u3307\u3308\u3309\u330a\u330b\u330c\u330d\u330e\u330f\u3310\u3311\u3312\u3313\u3314\u3315\u3316\u3317\u3318\u3319\u331a\u331b\u331c\u331d\u331e\u331f\u3320\u3321\u3322\u3323\u3324\u3325\u3326\u3327\u3328\u3329\u332a\u332b\u332c\u332d\u332e\u332f\u3330\u3331\u3332\u3333\u3334\u3335\u3336\u3337\u3338\u3339\u333a\u333b\u333c\u333d\u333e\u333f\u3340\u3341\u3342\u3343\u3344\u3345\u3346\u3347\u3348\u3349\u334a\u334b\u334c\u334d\u334e\u334f\u3350\u3351\u3352\u3353\u3354\u3355\u3356\u3357\u3358\u3359\u335a\u335b\u335c\u335d\u335e\u335f\u3360\u3361\u3362\u3363\u3364\u3365\u3366\u3367\u3368\u3369\u336a\u336b\u336c\u336d\u336e\u336f\u3370\u3371\u3372\u3373\u3374\u3375\u3376\u3377\u3378\u3379\u337a\u337b\u337c\u337d\u337e\u337f\u3380\u3381\u3382\u3383\u3384\u3385\u3386\u3387\u3388\u3389\u338a\u338b\u338c\u338d\u338e\u338f\u3390\u3391\u3392\u3393\u3394\u3395\u3396\u3397\u3398\u3399\u339a\u339b\u339c\u339d\u339e\u339f\u33a0\u33a1\u33a2\u33a3\u33a4\u33a5\u33a6\u33a7\u33a8\u33a9\u33aa\u33ab\u33ac\u33ad\u33ae\u33af\u33b0\u33b1\u33b2\u33b3\u33b4\u33b5\u33b6\u33b7\u33b8\u33b9\u33ba\u33bb\u33bc\u33bd\u33be\u33bf\u33c0\u33c1\u33c2\u33c3\u33c4\u33c5\u33c6\u33c7\u33c8\u33c9\u33ca\u33cb\u33cc\u33cd\u33ce\u33cf\u33d0\u33d1\u33d2\u33d3\u33d4\u33d5\u33d6\u33d7\u33d8\u33d9\u33da\u33db\u33dc\u33dd\u33de\u33df\u33e0\u33e1\u33e2\u33e3\u33e4\u33e5\u33e6\u33e7\u33e8\u33e9\u33ea\u33eb\u33ec\u33ed\u33ee\u33ef\u33f0\u33f1\u33f2\u33f3\u33f4\u33f5\u33f6\u33f7\u33f8\u33f9\u33fa\u33fb\u33fc\u33fd\u33fe\u33ff\u4dc0\u4dc1\u4dc2\u4dc3\u4dc4\u4dc5\u4dc6\u4dc7\u4dc8\u4dc9\u4dca\u4dcb\u4dcc\u4dcd\u4dce\u4dcf\u4dd0\u4dd1\u4dd2\u4dd3\u4dd4\u4dd5\u4dd6\u4dd7\u4dd8\u4dd9\u4dda\u4ddb\u4ddc\u4ddd\u4dde\u4ddf\u4de0\u4de1\u4de2\u4de3\u4de4\u4de5\u4de6\u4de7\u4de8\u4de9\u4dea\u4deb\u4dec\u4ded\u4dee\u4def\u4df0\u4df1\u4df2\u4df3\u4df4\u4df5\u4df6\u4df7\u4df8\u4df9\u4dfa\u4dfb\u4dfc\u4dfd\u4dfe\u4dff\ua490\ua491\ua492\ua493\ua494\ua495\ua496\ua497\ua498\ua499\ua49a\ua49b\ua49c\ua49d\ua49e\ua49f\ua4a0\ua4a1\ua4a2\ua4a3\ua4a4\ua4a5\ua4a6\ua4a7\ua4a8\ua4a9\ua4aa\ua4ab\ua4ac\ua4ad\ua4ae\ua4af\ua4b0\ua4b1\ua4b2\ua4b3\ua4b4\ua4b5\ua4b6\ua4b7\ua4b8\ua4b9\ua4ba\ua4bb\ua4bc\ua4bd\ua4be\ua4bf\ua4c0\ua4c1\ua4c2\ua4c3\ua4c4\ua4c5\ua4c6\ua828\ua829\ua82a\ua82b\ufdfd\uffe4\uffe8\uffed\uffee\ufffc\ufffd'
+
+Zl = u'\u2028'
+
+Zp = u'\u2029'
+
+Zs = u' \xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
+
+cats = ['Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs']
+
+def combine(*args):
+ return u''.join([globals()[cat] for cat in args])
+
+xid_start = u'\u0041-\u005A\u005F\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u01BA\u01BB\u01BC-\u01BF\u01C0-\u01C3\u01C4-\u0241\u0250-\u02AF\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EE\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03F5\u03F7-\u0481\u048A-\u04CE\u04D0-\u04F9\u0500-\u050F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0640\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u076D\u0780-\u07A5\u07B1\u0904-\u0939\u093D\u0950\u0958-\u0961\u097D\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E40-\u0E45\u0E46\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDD\u0F00\u0F40-\u0F47\u0F49-\u0F6A\u0F88-\u0F8B\u1000-\u1021\u1023-\u1027\u1029-\u102A\u1050-\u1055\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1842\u1843\u1844-\u1877\u1880-\u18A8\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19C1-\u19C7\u1A00-\u1A16\u1D00-\u1D2B\u1D2C-\u1D61\u1D62-\u1D77\u1D78\u1D79-\u1D9A\u1D9B-\u1DBF\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u2094\u2102\u2107\u210A-\u2113\u2115\u2118\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212E\u212F-\u2131\u2133-\u2134\u2135-\u2138\u2139\u213C-\u213F\u2145-\u2149\u2160-\u2183\u2C00-\u2C2E\u2C30-\u2C5E\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005\u3006\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303A\u303B\u303C\u3041-\u3096\u309D-\u309E\u309F\u30A1-\u30FA\u30FC-\u30FE\u30FF\u3105-\u312C\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FBB\uA000-\uA014\uA015\uA016-\uA48C\uA800-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uAC00-\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFC5D\uFC64-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDF9\uFE71\uFE73\uFE77\uFE79\uFE7B\uFE7D\uFE7F-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFF6F\uFF70\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC'
+
+xid_continue = u'\u0030-\u0039\u0041-\u005A\u005F\u0061-\u007A\u00AA\u00B5\u00B7\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u01BA\u01BB\u01BC-\u01BF\u01C0-\u01C3\u01C4-\u0241\u0250-\u02AF\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EE\u0300-\u036F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03F5\u03F7-\u0481\u0483-\u0486\u048A-\u04CE\u04D0-\u04F9\u0500-\u050F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05B9\u05BB-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u0615\u0621-\u063A\u0640\u0641-\u064A\u064B-\u065E\u0660-\u0669\u066E-\u066F\u0670\u0671-\u06D3\u06D5\u06D6-\u06DC\u06DF-\u06E4\u06E5-\u06E6\u06E7-\u06E8\u06EA-\u06ED\u06EE-\u06EF\u06F0-\u06F9\u06FA-\u06FC\u06FF\u0710\u0711\u0712-\u072F\u0730-\u074A\u074D-\u076D\u0780-\u07A5\u07A6-\u07B0\u07B1\u0901-\u0902\u0903\u0904-\u0939\u093C\u093D\u093E-\u0940\u0941-\u0948\u0949-\u094C\u094D\u0950\u0951-\u0954\u0958-\u0961\u0962-\u0963\u0966-\u096F\u097D\u0981\u0982-\u0983\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC\u09BD\u09BE-\u09C0\u09C1-\u09C4\u09C7-\u09C8\u09CB-\u09CC\u09CD\u09CE\u09D7\u09DC-\u09DD\u09DF-\u09E1\u09E2-\u09E3\u09E6-\u09EF\u09F0-\u09F1\u0A01-\u0A02\u0A03\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A3C\u0A3E-\u0A40\u0A41-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A70-\u0A71\u0A72-\u0A74\u0A81-\u0A82\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABC\u0ABD\u0ABE-\u0AC0\u0AC1-\u0AC5\u0AC7-\u0AC8\u0AC9\u0ACB-\u0ACC\u0ACD\u0AD0\u0AE0-\u0AE1\u0AE2-\u0AE3\u0AE6-\u0AEF\u0B01\u0B02-\u0B03\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3C\u0B3D\u0B3E\u0B3F\u0B40\u0B41-\u0B43\u0B47-\u0B48\u0B4B-\u0B4C\u0B4D\u0B56\u0B57\u0B5C-\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BBF\u0BC0\u0BC1-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BCD\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3E-\u0C40\u0C41-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C60-\u0C61\u0C66-\u0C6F\u0C82-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC\u0CBD\u0CBE\u0CBF\u0CC0-\u0CC4\u0CC6\u0CC7-\u0CC8\u0CCA-\u0CCB\u0CCC-\u0CCD\u0CD5-\u0CD6\u0CDE\u0CE0-\u0CE1\u0CE6-\u0CEF\u0D02-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3E-\u0D40\u0D41-\u0D43\u0D46-\u0D48\u0D4A-\u0D4C\u0D4D\u0D57\u0D60-\u0D61\u0D66-\u0D6F\u0D82-\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD1\u0DD2-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2-\u0DF3\u0E01-\u0E30\u0E31\u0E32-\u0E33\u0E34-\u0E3A\u0E40-\u0E45\u0E46\u0E47-\u0E4E\u0E50-\u0E59\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB1\u0EB2-\u0EB3\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDD\u0F00\u0F18-\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F3F\u0F40-\u0F47\u0F49-\u0F6A\u0F71-\u0F7E\u0F7F\u0F80-\u0F84\u0F86-\u0F87\u0F88-\u0F8B\u0F90-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1021\u1023-\u1027\u1029-\u102A\u102C\u102D-\u1030\u1031\u1032\u1036-\u1037\u1038\u1039\u1040-\u1049\u1050-\u1055\u1056-\u1057\u1058-\u1059\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1712-\u1714\u1720-\u1731\u1732-\u1734\u1740-\u1751\u1752-\u1753\u1760-\u176C\u176E-\u1770\u1772-\u1773\u1780-\u17B3\u17B6\u17B7-\u17BD\u17BE-\u17C5\u17C6\u17C7-\u17C8\u17C9-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1842\u1843\u1844-\u1877\u1880-\u18A8\u18A9\u1900-\u191C\u1920-\u1922\u1923-\u1926\u1927-\u1928\u1929-\u192B\u1930-\u1931\u1932\u1933-\u1938\u1939-\u193B\u1946-\u194F\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19B0-\u19C0\u19C1-\u19C7\u19C8-\u19C9\u19D0-\u19D9\u1A00-\u1A16\u1A17-\u1A18\u1A19-\u1A1B\u1D00-\u1D2B\u1D2C-\u1D61\u1D62-\u1D77\u1D78\u1D79-\u1D9A\u1D9B-\u1DBF\u1DC0-\u1DC3\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F-\u2040\u2054\u2071\u207F\u2090-\u2094\u20D0-\u20DC\u20E1\u20E5-\u20EB\u2102\u2107\u210A-\u2113\u2115\u2118\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212E\u212F-\u2131\u2133-\u2134\u2135-\u2138\u2139\u213C-\u213F\u2145-\u2149\u2160-\u2183\u2C00-\u2C2E\u2C30-\u2C5E\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005\u3006\u3007\u3021-\u3029\u302A-\u302F\u3031-\u3035\u3038-\u303A\u303B\u303C\u3041-\u3096\u3099-\u309A\u309D-\u309E\u309F\u30A1-\u30FA\u30FC-\u30FE\u30FF\u3105-\u312C\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FBB\uA000-\uA014\uA015\uA016-\uA48C\uA800-\uA801\uA802\uA803-\uA805\uA806\uA807-\uA80A\uA80B\uA80C-\uA822\uA823-\uA824\uA825-\uA826\uA827\uAC00-\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1E\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFC5D\uFC64-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDF9\uFE00-\uFE0F\uFE20-\uFE23\uFE33-\uFE34\uFE4D-\uFE4F\uFE71\uFE73\uFE77\uFE79\uFE7B\uFE7D\uFE7F-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFF6F\uFF70\uFF71-\uFF9D\uFF9E-\uFF9F\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC'
+
+def allexcept(*args):
+ newcats = cats[:]
+ for arg in args:
+ newcats.remove(arg)
+ return u''.join([globals()[cat] for cat in newcats])
+
+if __name__ == '__main__':
+ import unicodedata
+
+ categories = {}
+
+ f = open(__file__.rstrip('co'))
+ try:
+ content = f.read()
+ finally:
+ f.close()
+
+ header = content[:content.find('Cc =')]
+ footer = content[content.find("def combine("):]
+
+ for code in range(65535):
+ c = unichr(code)
+ cat = unicodedata.category(c)
+ categories.setdefault(cat, []).append(c)
+
+ f = open(__file__, 'w')
+ f.write(header)
+
+ for cat in sorted(categories):
+ val = u''.join(categories[cat])
+ if cat == 'Cs':
+ # Jython can't handle isolated surrogates
+ f.write("""\
+try:
+ Cs = eval(r"%r")
+except UnicodeDecodeError:
+ Cs = '' # Jython can't handle isolated surrogates\n\n""" % val)
+ else:
+ f.write('%s = %r\n\n' % (cat, val))
+ f.write('cats = %r\n\n' % sorted(categories.keys()))
+
+ f.write(footer)
+ f.close()
diff --git a/websdk/jinja2/bccache.py b/websdk/jinja2/bccache.py
new file mode 100644
index 0000000..0b0ccad
--- /dev/null
+++ b/websdk/jinja2/bccache.py
@@ -0,0 +1,301 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.bccache
+ ~~~~~~~~~~~~~~
+
+ This module implements the bytecode cache system Jinja is optionally
+ using. This is useful if you have very complex template situations and
+ the compiliation of all those templates slow down your application too
+ much.
+
+ Situations where this is useful are often forking web applications that
+ are initialized on the first request.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD.
+"""
+from os import path, listdir
+import sys
+import marshal
+import tempfile
+import cPickle as pickle
+import fnmatch
+try:
+ from hashlib import sha1
+except ImportError:
+ from sha import new as sha1
+from jinja2.utils import open_if_exists
+
+
+# marshal works better on 3.x, one hack less required
+if sys.version_info > (3, 0):
+ from io import BytesIO
+ marshal_dump = marshal.dump
+ marshal_load = marshal.load
+else:
+ from cStringIO import StringIO as BytesIO
+
+ def marshal_dump(code, f):
+ if isinstance(f, file):
+ marshal.dump(code, f)
+ else:
+ f.write(marshal.dumps(code))
+
+ def marshal_load(f):
+ if isinstance(f, file):
+ return marshal.load(f)
+ return marshal.loads(f.read())
+
+
+bc_version = 2
+
+# magic version used to only change with new jinja versions. With 2.6
+# we change this to also take Python version changes into account. The
+# reason for this is that Python tends to segfault if fed earlier bytecode
+# versions because someone thought it would be a good idea to reuse opcodes
+# or make Python incompatible with earlier versions.
+bc_magic = 'j2'.encode('ascii') + \
+ pickle.dumps(bc_version, 2) + \
+ pickle.dumps((sys.version_info[0] << 24) | sys.version_info[1])
+
+
+class Bucket(object):
+ """Buckets are used to store the bytecode for one template. It's created
+ and initialized by the bytecode cache and passed to the loading functions.
+
+ The buckets get an internal checksum from the cache assigned and use this
+ to automatically reject outdated cache material. Individual bytecode
+ cache subclasses don't have to care about cache invalidation.
+ """
+
+ def __init__(self, environment, key, checksum):
+ self.environment = environment
+ self.key = key
+ self.checksum = checksum
+ self.reset()
+
+ def reset(self):
+ """Resets the bucket (unloads the bytecode)."""
+ self.code = None
+
+ def load_bytecode(self, f):
+ """Loads bytecode from a file or file like object."""
+ # make sure the magic header is correct
+ magic = f.read(len(bc_magic))
+ if magic != bc_magic:
+ self.reset()
+ return
+ # the source code of the file changed, we need to reload
+ checksum = pickle.load(f)
+ if self.checksum != checksum:
+ self.reset()
+ return
+ self.code = marshal_load(f)
+
+ def write_bytecode(self, f):
+ """Dump the bytecode into the file or file like object passed."""
+ if self.code is None:
+ raise TypeError('can\'t write empty bucket')
+ f.write(bc_magic)
+ pickle.dump(self.checksum, f, 2)
+ marshal_dump(self.code, f)
+
+ def bytecode_from_string(self, string):
+ """Load bytecode from a string."""
+ self.load_bytecode(BytesIO(string))
+
+ def bytecode_to_string(self):
+ """Return the bytecode as string."""
+ out = BytesIO()
+ self.write_bytecode(out)
+ return out.getvalue()
+
+
+class BytecodeCache(object):
+ """To implement your own bytecode cache you have to subclass this class
+ and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of
+ these methods are passed a :class:`~jinja2.bccache.Bucket`.
+
+ A very basic bytecode cache that saves the bytecode on the file system::
+
+ from os import path
+
+ class MyCache(BytecodeCache):
+
+ def __init__(self, directory):
+ self.directory = directory
+
+ def load_bytecode(self, bucket):
+ filename = path.join(self.directory, bucket.key)
+ if path.exists(filename):
+ with open(filename, 'rb') as f:
+ bucket.load_bytecode(f)
+
+ def dump_bytecode(self, bucket):
+ filename = path.join(self.directory, bucket.key)
+ with open(filename, 'wb') as f:
+ bucket.write_bytecode(f)
+
+ A more advanced version of a filesystem based bytecode cache is part of
+ Jinja2.
+ """
+
+ def load_bytecode(self, bucket):
+ """Subclasses have to override this method to load bytecode into a
+ bucket. If they are not able to find code in the cache for the
+ bucket, it must not do anything.
+ """
+ raise NotImplementedError()
+
+ def dump_bytecode(self, bucket):
+ """Subclasses have to override this method to write the bytecode
+ from a bucket back to the cache. If it unable to do so it must not
+ fail silently but raise an exception.
+ """
+ raise NotImplementedError()
+
+ def clear(self):
+ """Clears the cache. This method is not used by Jinja2 but should be
+ implemented to allow applications to clear the bytecode cache used
+ by a particular environment.
+ """
+
+ def get_cache_key(self, name, filename=None):
+ """Returns the unique hash key for this template name."""
+ hash = sha1(name.encode('utf-8'))
+ if filename is not None:
+ filename = '|' + filename
+ if isinstance(filename, unicode):
+ filename = filename.encode('utf-8')
+ hash.update(filename)
+ return hash.hexdigest()
+
+ def get_source_checksum(self, source):
+ """Returns a checksum for the source."""
+ return sha1(source.encode('utf-8')).hexdigest()
+
+ def get_bucket(self, environment, name, filename, source):
+ """Return a cache bucket for the given template. All arguments are
+ mandatory but filename may be `None`.
+ """
+ key = self.get_cache_key(name, filename)
+ checksum = self.get_source_checksum(source)
+ bucket = Bucket(environment, key, checksum)
+ self.load_bytecode(bucket)
+ return bucket
+
+ def set_bucket(self, bucket):
+ """Put the bucket into the cache."""
+ self.dump_bytecode(bucket)
+
+
+class FileSystemBytecodeCache(BytecodeCache):
+ """A bytecode cache that stores bytecode on the filesystem. It accepts
+ two arguments: The directory where the cache items are stored and a
+ pattern string that is used to build the filename.
+
+ If no directory is specified the system temporary items folder is used.
+
+ The pattern can be used to have multiple separate caches operate on the
+ same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s``
+ is replaced with the cache key.
+
+ >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
+
+ This bytecode cache supports clearing of the cache using the clear method.
+ """
+
+ def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
+ if directory is None:
+ directory = tempfile.gettempdir()
+ self.directory = directory
+ self.pattern = pattern
+
+ def _get_cache_filename(self, bucket):
+ return path.join(self.directory, self.pattern % bucket.key)
+
+ def load_bytecode(self, bucket):
+ f = open_if_exists(self._get_cache_filename(bucket), 'rb')
+ if f is not None:
+ try:
+ bucket.load_bytecode(f)
+ finally:
+ f.close()
+
+ def dump_bytecode(self, bucket):
+ f = open(self._get_cache_filename(bucket), 'wb')
+ try:
+ bucket.write_bytecode(f)
+ finally:
+ f.close()
+
+ def clear(self):
+ # imported lazily here because google app-engine doesn't support
+ # write access on the file system and the function does not exist
+ # normally.
+ from os import remove
+ files = fnmatch.filter(listdir(self.directory), self.pattern % '*')
+ for filename in files:
+ try:
+ remove(path.join(self.directory, filename))
+ except OSError:
+ pass
+
+
+class MemcachedBytecodeCache(BytecodeCache):
+ """This class implements a bytecode cache that uses a memcache cache for
+ storing the information. It does not enforce a specific memcache library
+ (tummy's memcache or cmemcache) but will accept any class that provides
+ the minimal interface required.
+
+ Libraries compatible with this class:
+
+ - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
+ - `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_
+ - `cmemcache <http://gijsbert.org/cmemcache/>`_
+
+ (Unfortunately the django cache interface is not compatible because it
+ does not support storing binary data, only unicode. You can however pass
+ the underlying cache client to the bytecode cache which is available
+ as `django.core.cache.cache._client`.)
+
+ The minimal interface for the client passed to the constructor is this:
+
+ .. class:: MinimalClientInterface
+
+ .. method:: set(key, value[, timeout])
+
+ Stores the bytecode in the cache. `value` is a string and
+ `timeout` the timeout of the key. If timeout is not provided
+ a default timeout or no timeout should be assumed, if it's
+ provided it's an integer with the number of seconds the cache
+ item should exist.
+
+ .. method:: get(key)
+
+ Returns the value for the cache key. If the item does not
+ exist in the cache the return value must be `None`.
+
+ The other arguments to the constructor are the prefix for all keys that
+ is added before the actual cache key and the timeout for the bytecode in
+ the cache system. We recommend a high (or no) timeout.
+
+ This bytecode cache does not support clearing of used items in the cache.
+ The clear method is a no-operation function.
+ """
+
+ def __init__(self, client, prefix='jinja2/bytecode/', timeout=None):
+ self.client = client
+ self.prefix = prefix
+ self.timeout = timeout
+
+ def load_bytecode(self, bucket):
+ code = self.client.get(self.prefix + bucket.key)
+ if code is not None:
+ bucket.bytecode_from_string(code)
+
+ def dump_bytecode(self, bucket):
+ args = (self.prefix + bucket.key, bucket.bytecode_to_string())
+ if self.timeout is not None:
+ args += (self.timeout,)
+ self.client.set(*args)
diff --git a/websdk/jinja2/compiler.py b/websdk/jinja2/compiler.py
new file mode 100644
index 0000000..b21cb38
--- /dev/null
+++ b/websdk/jinja2/compiler.py
@@ -0,0 +1,1649 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.compiler
+ ~~~~~~~~~~~~~~~
+
+ Compiles nodes into python code.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+from cStringIO import StringIO
+from itertools import chain
+from copy import deepcopy
+from jinja2 import nodes
+from jinja2.nodes import EvalContext
+from jinja2.visitor import NodeVisitor
+from jinja2.exceptions import TemplateAssertionError
+from jinja2.utils import Markup, concat, escape, is_python_keyword, next
+
+
+operators = {
+ 'eq': '==',
+ 'ne': '!=',
+ 'gt': '>',
+ 'gteq': '>=',
+ 'lt': '<',
+ 'lteq': '<=',
+ 'in': 'in',
+ 'notin': 'not in'
+}
+
+try:
+ exec '(0 if 0 else 0)'
+except SyntaxError:
+ have_condexpr = False
+else:
+ have_condexpr = True
+
+
+# what method to iterate over items do we want to use for dict iteration
+# in generated code? on 2.x let's go with iteritems, on 3.x with items
+if hasattr(dict, 'iteritems'):
+ dict_item_iter = 'iteritems'
+else:
+ dict_item_iter = 'items'
+
+
+# does if 0: dummy(x) get us x into the scope?
+def unoptimize_before_dead_code():
+ x = 42
+ def f():
+ if 0: dummy(x)
+ return f
+unoptimize_before_dead_code = bool(unoptimize_before_dead_code().func_closure)
+
+
+def generate(node, environment, name, filename, stream=None,
+ defer_init=False):
+ """Generate the python source for a node tree."""
+ if not isinstance(node, nodes.Template):
+ raise TypeError('Can\'t compile non template nodes')
+ generator = CodeGenerator(environment, name, filename, stream, defer_init)
+ generator.visit(node)
+ if stream is None:
+ return generator.stream.getvalue()
+
+
+def has_safe_repr(value):
+ """Does the node have a safe representation?"""
+ if value is None or value is NotImplemented or value is Ellipsis:
+ return True
+ if isinstance(value, (bool, int, long, float, complex, basestring,
+ xrange, Markup)):
+ return True
+ if isinstance(value, (tuple, list, set, frozenset)):
+ for item in value:
+ if not has_safe_repr(item):
+ return False
+ return True
+ elif isinstance(value, dict):
+ for key, value in value.iteritems():
+ if not has_safe_repr(key):
+ return False
+ if not has_safe_repr(value):
+ return False
+ return True
+ return False
+
+
+def find_undeclared(nodes, names):
+ """Check if the names passed are accessed undeclared. The return value
+ is a set of all the undeclared names from the sequence of names found.
+ """
+ visitor = UndeclaredNameVisitor(names)
+ try:
+ for node in nodes:
+ visitor.visit(node)
+ except VisitorExit:
+ pass
+ return visitor.undeclared
+
+
+class Identifiers(object):
+ """Tracks the status of identifiers in frames."""
+
+ def __init__(self):
+ # variables that are known to be declared (probably from outer
+ # frames or because they are special for the frame)
+ self.declared = set()
+
+ # undeclared variables from outer scopes
+ self.outer_undeclared = set()
+
+ # names that are accessed without being explicitly declared by
+ # this one or any of the outer scopes. Names can appear both in
+ # declared and undeclared.
+ self.undeclared = set()
+
+ # names that are declared locally
+ self.declared_locally = set()
+
+ # names that are declared by parameters
+ self.declared_parameter = set()
+
+ def add_special(self, name):
+ """Register a special name like `loop`."""
+ self.undeclared.discard(name)
+ self.declared.add(name)
+
+ def is_declared(self, name):
+ """Check if a name is declared in this or an outer scope."""
+ if name in self.declared_locally or name in self.declared_parameter:
+ return True
+ return name in self.declared
+
+ def copy(self):
+ return deepcopy(self)
+
+
+class Frame(object):
+ """Holds compile time information for us."""
+
+ def __init__(self, eval_ctx, parent=None):
+ self.eval_ctx = eval_ctx
+ self.identifiers = Identifiers()
+
+ # a toplevel frame is the root + soft frames such as if conditions.
+ self.toplevel = False
+
+ # the root frame is basically just the outermost frame, so no if
+ # conditions. This information is used to optimize inheritance
+ # situations.
+ self.rootlevel = False
+
+ # in some dynamic inheritance situations the compiler needs to add
+ # write tests around output statements.
+ self.require_output_check = parent and parent.require_output_check
+
+ # inside some tags we are using a buffer rather than yield statements.
+ # this for example affects {% filter %} or {% macro %}. If a frame
+ # is buffered this variable points to the name of the list used as
+ # buffer.
+ self.buffer = None
+
+ # the name of the block we're in, otherwise None.
+ self.block = parent and parent.block or None
+
+ # a set of actually assigned names
+ self.assigned_names = set()
+
+ # the parent of this frame
+ self.parent = parent
+
+ if parent is not None:
+ self.identifiers.declared.update(
+ parent.identifiers.declared |
+ parent.identifiers.declared_parameter |
+ parent.assigned_names
+ )
+ self.identifiers.outer_undeclared.update(
+ parent.identifiers.undeclared -
+ self.identifiers.declared
+ )
+ self.buffer = parent.buffer
+
+ def copy(self):
+ """Create a copy of the current one."""
+ rv = object.__new__(self.__class__)
+ rv.__dict__.update(self.__dict__)
+ rv.identifiers = object.__new__(self.identifiers.__class__)
+ rv.identifiers.__dict__.update(self.identifiers.__dict__)
+ return rv
+
+ def inspect(self, nodes):
+ """Walk the node and check for identifiers. If the scope is hard (eg:
+ enforce on a python level) overrides from outer scopes are tracked
+ differently.
+ """
+ visitor = FrameIdentifierVisitor(self.identifiers)
+ for node in nodes:
+ visitor.visit(node)
+
+ def find_shadowed(self, extra=()):
+ """Find all the shadowed names. extra is an iterable of variables
+ that may be defined with `add_special` which may occour scoped.
+ """
+ i = self.identifiers
+ return (i.declared | i.outer_undeclared) & \
+ (i.declared_locally | i.declared_parameter) | \
+ set(x for x in extra if i.is_declared(x))
+
+ def inner(self):
+ """Return an inner frame."""
+ return Frame(self.eval_ctx, self)
+
+ def soft(self):
+ """Return a soft frame. A soft frame may not be modified as
+ standalone thing as it shares the resources with the frame it
+ was created of, but it's not a rootlevel frame any longer.
+ """
+ rv = self.copy()
+ rv.rootlevel = False
+ return rv
+
+ __copy__ = copy
+
+
+class VisitorExit(RuntimeError):
+ """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
+
+
+class DependencyFinderVisitor(NodeVisitor):
+ """A visitor that collects filter and test calls."""
+
+ def __init__(self):
+ self.filters = set()
+ self.tests = set()
+
+ def visit_Filter(self, node):
+ self.generic_visit(node)
+ self.filters.add(node.name)
+
+ def visit_Test(self, node):
+ self.generic_visit(node)
+ self.tests.add(node.name)
+
+ def visit_Block(self, node):
+ """Stop visiting at blocks."""
+
+
+class UndeclaredNameVisitor(NodeVisitor):
+ """A visitor that checks if a name is accessed without being
+ declared. This is different from the frame visitor as it will
+ not stop at closure frames.
+ """
+
+ def __init__(self, names):
+ self.names = set(names)
+ self.undeclared = set()
+
+ def visit_Name(self, node):
+ if node.ctx == 'load' and node.name in self.names:
+ self.undeclared.add(node.name)
+ if self.undeclared == self.names:
+ raise VisitorExit()
+ else:
+ self.names.discard(node.name)
+
+ def visit_Block(self, node):
+ """Stop visiting a blocks."""
+
+
+class FrameIdentifierVisitor(NodeVisitor):
+ """A visitor for `Frame.inspect`."""
+
+ def __init__(self, identifiers):
+ self.identifiers = identifiers
+
+ def visit_Name(self, node):
+ """All assignments to names go through this function."""
+ if node.ctx == 'store':
+ self.identifiers.declared_locally.add(node.name)
+ elif node.ctx == 'param':
+ self.identifiers.declared_parameter.add(node.name)
+ elif node.ctx == 'load' and not \
+ self.identifiers.is_declared(node.name):
+ self.identifiers.undeclared.add(node.name)
+
+ def visit_If(self, node):
+ self.visit(node.test)
+ real_identifiers = self.identifiers
+
+ old_names = real_identifiers.declared_locally | \
+ real_identifiers.declared_parameter
+
+ def inner_visit(nodes):
+ if not nodes:
+ return set()
+ self.identifiers = real_identifiers.copy()
+ for subnode in nodes:
+ self.visit(subnode)
+ rv = self.identifiers.declared_locally - old_names
+ # we have to remember the undeclared variables of this branch
+ # because we will have to pull them.
+ real_identifiers.undeclared.update(self.identifiers.undeclared)
+ self.identifiers = real_identifiers
+ return rv
+
+ body = inner_visit(node.body)
+ else_ = inner_visit(node.else_ or ())
+
+ # the differences between the two branches are also pulled as
+ # undeclared variables
+ real_identifiers.undeclared.update(body.symmetric_difference(else_) -
+ real_identifiers.declared)
+
+ # remember those that are declared.
+ real_identifiers.declared_locally.update(body | else_)
+
+ def visit_Macro(self, node):
+ self.identifiers.declared_locally.add(node.name)
+
+ def visit_Import(self, node):
+ self.generic_visit(node)
+ self.identifiers.declared_locally.add(node.target)
+
+ def visit_FromImport(self, node):
+ self.generic_visit(node)
+ for name in node.names:
+ if isinstance(name, tuple):
+ self.identifiers.declared_locally.add(name[1])
+ else:
+ self.identifiers.declared_locally.add(name)
+
+ def visit_Assign(self, node):
+ """Visit assignments in the correct order."""
+ self.visit(node.node)
+ self.visit(node.target)
+
+ def visit_For(self, node):
+ """Visiting stops at for blocks. However the block sequence
+ is visited as part of the outer scope.
+ """
+ self.visit(node.iter)
+
+ def visit_CallBlock(self, node):
+ self.visit(node.call)
+
+ def visit_FilterBlock(self, node):
+ self.visit(node.filter)
+
+ def visit_Scope(self, node):
+ """Stop visiting at scopes."""
+
+ def visit_Block(self, node):
+ """Stop visiting at blocks."""
+
+
+class CompilerExit(Exception):
+ """Raised if the compiler encountered a situation where it just
+ doesn't make sense to further process the code. Any block that
+ raises such an exception is not further processed.
+ """
+
+
+class CodeGenerator(NodeVisitor):
+
+ def __init__(self, environment, name, filename, stream=None,
+ defer_init=False):
+ if stream is None:
+ stream = StringIO()
+ self.environment = environment
+ self.name = name
+ self.filename = filename
+ self.stream = stream
+ self.created_block_context = False
+ self.defer_init = defer_init
+
+ # aliases for imports
+ self.import_aliases = {}
+
+ # a registry for all blocks. Because blocks are moved out
+ # into the global python scope they are registered here
+ self.blocks = {}
+
+ # the number of extends statements so far
+ self.extends_so_far = 0
+
+ # some templates have a rootlevel extends. In this case we
+ # can safely assume that we're a child template and do some
+ # more optimizations.
+ self.has_known_extends = False
+
+ # the current line number
+ self.code_lineno = 1
+
+ # registry of all filters and tests (global, not block local)
+ self.tests = {}
+ self.filters = {}
+
+ # the debug information
+ self.debug_info = []
+ self._write_debug_info = None
+
+ # the number of new lines before the next write()
+ self._new_lines = 0
+
+ # the line number of the last written statement
+ self._last_line = 0
+
+ # true if nothing was written so far.
+ self._first_write = True
+
+ # used by the `temporary_identifier` method to get new
+ # unique, temporary identifier
+ self._last_identifier = 0
+
+ # the current indentation
+ self._indentation = 0
+
+ # -- Various compilation helpers
+
+ def fail(self, msg, lineno):
+ """Fail with a :exc:`TemplateAssertionError`."""
+ raise TemplateAssertionError(msg, lineno, self.name, self.filename)
+
+ def temporary_identifier(self):
+ """Get a new unique identifier."""
+ self._last_identifier += 1
+ return 't_%d' % self._last_identifier
+
+ def buffer(self, frame):
+ """Enable buffering for the frame from that point onwards."""
+ frame.buffer = self.temporary_identifier()
+ self.writeline('%s = []' % frame.buffer)
+
+ def return_buffer_contents(self, frame):
+ """Return the buffer contents of the frame."""
+ if frame.eval_ctx.volatile:
+ self.writeline('if context.eval_ctx.autoescape:')
+ self.indent()
+ self.writeline('return Markup(concat(%s))' % frame.buffer)
+ self.outdent()
+ self.writeline('else:')
+ self.indent()
+ self.writeline('return concat(%s)' % frame.buffer)
+ self.outdent()
+ elif frame.eval_ctx.autoescape:
+ self.writeline('return Markup(concat(%s))' % frame.buffer)
+ else:
+ self.writeline('return concat(%s)' % frame.buffer)
+
+ def indent(self):
+ """Indent by one."""
+ self._indentation += 1
+
+ def outdent(self, step=1):
+ """Outdent by step."""
+ self._indentation -= step
+
+ def start_write(self, frame, node=None):
+ """Yield or write into the frame buffer."""
+ if frame.buffer is None:
+ self.writeline('yield ', node)
+ else:
+ self.writeline('%s.append(' % frame.buffer, node)
+
+ def end_write(self, frame):
+ """End the writing process started by `start_write`."""
+ if frame.buffer is not None:
+ self.write(')')
+
+ def simple_write(self, s, frame, node=None):
+ """Simple shortcut for start_write + write + end_write."""
+ self.start_write(frame, node)
+ self.write(s)
+ self.end_write(frame)
+
+ def blockvisit(self, nodes, frame):
+ """Visit a list of nodes as block in a frame. If the current frame
+ is no buffer a dummy ``if 0: yield None`` is written automatically
+ unless the force_generator parameter is set to False.
+ """
+ if frame.buffer is None:
+ self.writeline('if 0: yield None')
+ else:
+ self.writeline('pass')
+ try:
+ for node in nodes:
+ self.visit(node, frame)
+ except CompilerExit:
+ pass
+
+ def write(self, x):
+ """Write a string into the output stream."""
+ if self._new_lines:
+ if not self._first_write:
+ self.stream.write('\n' * self._new_lines)
+ self.code_lineno += self._new_lines
+ if self._write_debug_info is not None:
+ self.debug_info.append((self._write_debug_info,
+ self.code_lineno))
+ self._write_debug_info = None
+ self._first_write = False
+ self.stream.write(' ' * self._indentation)
+ self._new_lines = 0
+ self.stream.write(x)
+
+ def writeline(self, x, node=None, extra=0):
+ """Combination of newline and write."""
+ self.newline(node, extra)
+ self.write(x)
+
+ def newline(self, node=None, extra=0):
+ """Add one or more newlines before the next write."""
+ self._new_lines = max(self._new_lines, 1 + extra)
+ if node is not None and node.lineno != self._last_line:
+ self._write_debug_info = node.lineno
+ self._last_line = node.lineno
+
+ def signature(self, node, frame, extra_kwargs=None):
+ """Writes a function call to the stream for the current node.
+ A leading comma is added automatically. The extra keyword
+ arguments may not include python keywords otherwise a syntax
+ error could occour. The extra keyword arguments should be given
+ as python dict.
+ """
+ # if any of the given keyword arguments is a python keyword
+ # we have to make sure that no invalid call is created.
+ kwarg_workaround = False
+ for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()):
+ if is_python_keyword(kwarg):
+ kwarg_workaround = True
+ break
+
+ for arg in node.args:
+ self.write(', ')
+ self.visit(arg, frame)
+
+ if not kwarg_workaround:
+ for kwarg in node.kwargs:
+ self.write(', ')
+ self.visit(kwarg, frame)
+ if extra_kwargs is not None:
+ for key, value in extra_kwargs.iteritems():
+ self.write(', %s=%s' % (key, value))
+ if node.dyn_args:
+ self.write(', *')
+ self.visit(node.dyn_args, frame)
+
+ if kwarg_workaround:
+ if node.dyn_kwargs is not None:
+ self.write(', **dict({')
+ else:
+ self.write(', **{')
+ for kwarg in node.kwargs:
+ self.write('%r: ' % kwarg.key)
+ self.visit(kwarg.value, frame)
+ self.write(', ')
+ if extra_kwargs is not None:
+ for key, value in extra_kwargs.iteritems():
+ self.write('%r: %s, ' % (key, value))
+ if node.dyn_kwargs is not None:
+ self.write('}, **')
+ self.visit(node.dyn_kwargs, frame)
+ self.write(')')
+ else:
+ self.write('}')
+
+ elif node.dyn_kwargs is not None:
+ self.write(', **')
+ self.visit(node.dyn_kwargs, frame)
+
+ def pull_locals(self, frame):
+ """Pull all the references identifiers into the local scope."""
+ for name in frame.identifiers.undeclared:
+ self.writeline('l_%s = context.resolve(%r)' % (name, name))
+
+ def pull_dependencies(self, nodes):
+ """Pull all the dependencies."""
+ visitor = DependencyFinderVisitor()
+ for node in nodes:
+ visitor.visit(node)
+ for dependency in 'filters', 'tests':
+ mapping = getattr(self, dependency)
+ for name in getattr(visitor, dependency):
+ if name not in mapping:
+ mapping[name] = self.temporary_identifier()
+ self.writeline('%s = environment.%s[%r]' %
+ (mapping[name], dependency, name))
+
+ def unoptimize_scope(self, frame):
+ """Disable Python optimizations for the frame."""
+ # XXX: this is not that nice but it has no real overhead. It
+ # mainly works because python finds the locals before dead code
+ # is removed. If that breaks we have to add a dummy function
+ # that just accepts the arguments and does nothing.
+ if frame.identifiers.declared:
+ self.writeline('%sdummy(%s)' % (
+ unoptimize_before_dead_code and 'if 0: ' or '',
+ ', '.join('l_' + name for name in frame.identifiers.declared)
+ ))
+
+ def push_scope(self, frame, extra_vars=()):
+ """This function returns all the shadowed variables in a dict
+ in the form name: alias and will write the required assignments
+ into the current scope. No indentation takes place.
+
+ This also predefines locally declared variables from the loop
+ body because under some circumstances it may be the case that
+
+ `extra_vars` is passed to `Frame.find_shadowed`.
+ """
+ aliases = {}
+ for name in frame.find_shadowed(extra_vars):
+ aliases[name] = ident = self.temporary_identifier()
+ self.writeline('%s = l_%s' % (ident, name))
+ to_declare = set()
+ for name in frame.identifiers.declared_locally:
+ if name not in aliases:
+ to_declare.add('l_' + name)
+ if to_declare:
+ self.writeline(' = '.join(to_declare) + ' = missing')
+ return aliases
+
+ def pop_scope(self, aliases, frame):
+ """Restore all aliases and delete unused variables."""
+ for name, alias in aliases.iteritems():
+ self.writeline('l_%s = %s' % (name, alias))
+ to_delete = set()
+ for name in frame.identifiers.declared_locally:
+ if name not in aliases:
+ to_delete.add('l_' + name)
+ if to_delete:
+ # we cannot use the del statement here because enclosed
+ # scopes can trigger a SyntaxError:
+ # a = 42; b = lambda: a; del a
+ self.writeline(' = '.join(to_delete) + ' = missing')
+
+ def function_scoping(self, node, frame, children=None,
+ find_special=True):
+ """In Jinja a few statements require the help of anonymous
+ functions. Those are currently macros and call blocks and in
+ the future also recursive loops. As there is currently
+ technical limitation that doesn't allow reading and writing a
+ variable in a scope where the initial value is coming from an
+ outer scope, this function tries to fall back with a common
+ error message. Additionally the frame passed is modified so
+ that the argumetns are collected and callers are looked up.
+
+ This will return the modified frame.
+ """
+ # we have to iterate twice over it, make sure that works
+ if children is None:
+ children = node.iter_child_nodes()
+ children = list(children)
+ func_frame = frame.inner()
+ func_frame.inspect(children)
+
+ # variables that are undeclared (accessed before declaration) and
+ # declared locally *and* part of an outside scope raise a template
+ # assertion error. Reason: we can't generate reasonable code from
+ # it without aliasing all the variables.
+ # this could be fixed in Python 3 where we have the nonlocal
+ # keyword or if we switch to bytecode generation
+ overriden_closure_vars = (
+ func_frame.identifiers.undeclared &
+ func_frame.identifiers.declared &
+ (func_frame.identifiers.declared_locally |
+ func_frame.identifiers.declared_parameter)
+ )
+ if overriden_closure_vars:
+ self.fail('It\'s not possible to set and access variables '
+ 'derived from an outer scope! (affects: %s)' %
+ ', '.join(sorted(overriden_closure_vars)), node.lineno)
+
+ # remove variables from a closure from the frame's undeclared
+ # identifiers.
+ func_frame.identifiers.undeclared -= (
+ func_frame.identifiers.undeclared &
+ func_frame.identifiers.declared
+ )
+
+ # no special variables for this scope, abort early
+ if not find_special:
+ return func_frame
+
+ func_frame.accesses_kwargs = False
+ func_frame.accesses_varargs = False
+ func_frame.accesses_caller = False
+ func_frame.arguments = args = ['l_' + x.name for x in node.args]
+
+ undeclared = find_undeclared(children, ('caller', 'kwargs', 'varargs'))
+
+ if 'caller' in undeclared:
+ func_frame.accesses_caller = True
+ func_frame.identifiers.add_special('caller')
+ args.append('l_caller')
+ if 'kwargs' in undeclared:
+ func_frame.accesses_kwargs = True
+ func_frame.identifiers.add_special('kwargs')
+ args.append('l_kwargs')
+ if 'varargs' in undeclared:
+ func_frame.accesses_varargs = True
+ func_frame.identifiers.add_special('varargs')
+ args.append('l_varargs')
+ return func_frame
+
+ def macro_body(self, node, frame, children=None):
+ """Dump the function def of a macro or call block."""
+ frame = self.function_scoping(node, frame, children)
+ # macros are delayed, they never require output checks
+ frame.require_output_check = False
+ args = frame.arguments
+ # XXX: this is an ugly fix for the loop nesting bug
+ # (tests.test_old_bugs.test_loop_call_bug). This works around
+ # a identifier nesting problem we have in general. It's just more
+ # likely to happen in loops which is why we work around it. The
+ # real solution would be "nonlocal" all the identifiers that are
+ # leaking into a new python frame and might be used both unassigned
+ # and assigned.
+ if 'loop' in frame.identifiers.declared:
+ args = args + ['l_loop=l_loop']
+ self.writeline('def macro(%s):' % ', '.join(args), node)
+ self.indent()
+ self.buffer(frame)
+ self.pull_locals(frame)
+ self.blockvisit(node.body, frame)
+ self.return_buffer_contents(frame)
+ self.outdent()
+ return frame
+
+ def macro_def(self, node, frame):
+ """Dump the macro definition for the def created by macro_body."""
+ arg_tuple = ', '.join(repr(x.name) for x in node.args)
+ name = getattr(node, 'name', None)
+ if len(node.args) == 1:
+ arg_tuple += ','
+ self.write('Macro(environment, macro, %r, (%s), (' %
+ (name, arg_tuple))
+ for arg in node.defaults:
+ self.visit(arg, frame)
+ self.write(', ')
+ self.write('), %r, %r, %r)' % (
+ bool(frame.accesses_kwargs),
+ bool(frame.accesses_varargs),
+ bool(frame.accesses_caller)
+ ))
+
+ def position(self, node):
+ """Return a human readable position for the node."""
+ rv = 'line %d' % node.lineno
+ if self.name is not None:
+ rv += ' in ' + repr(self.name)
+ return rv
+
+ # -- Statement Visitors
+
+ def visit_Template(self, node, frame=None):
+ assert frame is None, 'no root frame allowed'
+ eval_ctx = EvalContext(self.environment, self.name)
+
+ from jinja2.runtime import __all__ as exported
+ self.writeline('from __future__ import division')
+ self.writeline('from jinja2.runtime import ' + ', '.join(exported))
+ if not unoptimize_before_dead_code:
+ self.writeline('dummy = lambda *x: None')
+
+ # if we want a deferred initialization we cannot move the
+ # environment into a local name
+ envenv = not self.defer_init and ', environment=environment' or ''
+
+ # do we have an extends tag at all? If not, we can save some
+ # overhead by just not processing any inheritance code.
+ have_extends = node.find(nodes.Extends) is not None
+
+ # find all blocks
+ for block in node.find_all(nodes.Block):
+ if block.name in self.blocks:
+ self.fail('block %r defined twice' % block.name, block.lineno)
+ self.blocks[block.name] = block
+
+ # find all imports and import them
+ for import_ in node.find_all(nodes.ImportedName):
+ if import_.importname not in self.import_aliases:
+ imp = import_.importname
+ self.import_aliases[imp] = alias = self.temporary_identifier()
+ if '.' in imp:
+ module, obj = imp.rsplit('.', 1)
+ self.writeline('from %s import %s as %s' %
+ (module, obj, alias))
+ else:
+ self.writeline('import %s as %s' % (imp, alias))
+
+ # add the load name
+ self.writeline('name = %r' % self.name)
+
+ # generate the root render function.
+ self.writeline('def root(context%s):' % envenv, extra=1)
+
+ # process the root
+ frame = Frame(eval_ctx)
+ frame.inspect(node.body)
+ frame.toplevel = frame.rootlevel = True
+ frame.require_output_check = have_extends and not self.has_known_extends
+ self.indent()
+ if have_extends:
+ self.writeline('parent_template = None')
+ if 'self' in find_undeclared(node.body, ('self',)):
+ frame.identifiers.add_special('self')
+ self.writeline('l_self = TemplateReference(context)')
+ self.pull_locals(frame)
+ self.pull_dependencies(node.body)
+ self.blockvisit(node.body, frame)
+ self.outdent()
+
+ # make sure that the parent root is called.
+ if have_extends:
+ if not self.has_known_extends:
+ self.indent()
+ self.writeline('if parent_template is not None:')
+ self.indent()
+ self.writeline('for event in parent_template.'
+ 'root_render_func(context):')
+ self.indent()
+ self.writeline('yield event')
+ self.outdent(2 + (not self.has_known_extends))
+
+ # at this point we now have the blocks collected and can visit them too.
+ for name, block in self.blocks.iteritems():
+ block_frame = Frame(eval_ctx)
+ block_frame.inspect(block.body)
+ block_frame.block = name
+ self.writeline('def block_%s(context%s):' % (name, envenv),
+ block, 1)
+ self.indent()
+ undeclared = find_undeclared(block.body, ('self', 'super'))
+ if 'self' in undeclared:
+ block_frame.identifiers.add_special('self')
+ self.writeline('l_self = TemplateReference(context)')
+ if 'super' in undeclared:
+ block_frame.identifiers.add_special('super')
+ self.writeline('l_super = context.super(%r, '
+ 'block_%s)' % (name, name))
+ self.pull_locals(block_frame)
+ self.pull_dependencies(block.body)
+ self.blockvisit(block.body, block_frame)
+ self.outdent()
+
+ self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
+ for x in self.blocks),
+ extra=1)
+
+ # add a function that returns the debug info
+ self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x
+ in self.debug_info))
+
+ def visit_Block(self, node, frame):
+ """Call a block and register it for the template."""
+ level = 1
+ if frame.toplevel:
+ # if we know that we are a child template, there is no need to
+ # check if we are one
+ if self.has_known_extends:
+ return
+ if self.extends_so_far > 0:
+ self.writeline('if parent_template is None:')
+ self.indent()
+ level += 1
+ context = node.scoped and 'context.derived(locals())' or 'context'
+ self.writeline('for event in context.blocks[%r][0](%s):' % (
+ node.name, context), node)
+ self.indent()
+ self.simple_write('event', frame)
+ self.outdent(level)
+
+ def visit_Extends(self, node, frame):
+ """Calls the extender."""
+ if not frame.toplevel:
+ self.fail('cannot use extend from a non top-level scope',
+ node.lineno)
+
+ # if the number of extends statements in general is zero so
+ # far, we don't have to add a check if something extended
+ # the template before this one.
+ if self.extends_so_far > 0:
+
+ # if we have a known extends we just add a template runtime
+ # error into the generated code. We could catch that at compile
+ # time too, but i welcome it not to confuse users by throwing the
+ # same error at different times just "because we can".
+ if not self.has_known_extends:
+ self.writeline('if parent_template is not None:')
+ self.indent()
+ self.writeline('raise TemplateRuntimeError(%r)' %
+ 'extended multiple times')
+ self.outdent()
+
+ # if we have a known extends already we don't need that code here
+ # as we know that the template execution will end here.
+ if self.has_known_extends:
+ raise CompilerExit()
+
+ self.writeline('parent_template = environment.get_template(', node)
+ self.visit(node.template, frame)
+ self.write(', %r)' % self.name)
+ self.writeline('for name, parent_block in parent_template.'
+ 'blocks.%s():' % dict_item_iter)
+ self.indent()
+ self.writeline('context.blocks.setdefault(name, []).'
+ 'append(parent_block)')
+ self.outdent()
+
+ # if this extends statement was in the root level we can take
+ # advantage of that information and simplify the generated code
+ # in the top level from this point onwards
+ if frame.rootlevel:
+ self.has_known_extends = True
+
+ # and now we have one more
+ self.extends_so_far += 1
+
+ def visit_Include(self, node, frame):
+ """Handles includes."""
+ if node.with_context:
+ self.unoptimize_scope(frame)
+ if node.ignore_missing:
+ self.writeline('try:')
+ self.indent()
+
+ func_name = 'get_or_select_template'
+ if isinstance(node.template, nodes.Const):
+ if isinstance(node.template.value, basestring):
+ func_name = 'get_template'
+ elif isinstance(node.template.value, (tuple, list)):
+ func_name = 'select_template'
+ elif isinstance(node.template, (nodes.Tuple, nodes.List)):
+ func_name = 'select_template'
+
+ self.writeline('template = environment.%s(' % func_name, node)
+ self.visit(node.template, frame)
+ self.write(', %r)' % self.name)
+ if node.ignore_missing:
+ self.outdent()
+ self.writeline('except TemplateNotFound:')
+ self.indent()
+ self.writeline('pass')
+ self.outdent()
+ self.writeline('else:')
+ self.indent()
+
+ if node.with_context:
+ self.writeline('for event in template.root_render_func('
+ 'template.new_context(context.parent, True, '
+ 'locals())):')
+ else:
+ self.writeline('for event in template.module._body_stream:')
+
+ self.indent()
+ self.simple_write('event', frame)
+ self.outdent()
+
+ if node.ignore_missing:
+ self.outdent()
+
+ def visit_Import(self, node, frame):
+ """Visit regular imports."""
+ if node.with_context:
+ self.unoptimize_scope(frame)
+ self.writeline('l_%s = ' % node.target, node)
+ if frame.toplevel:
+ self.write('context.vars[%r] = ' % node.target)
+ self.write('environment.get_template(')
+ self.visit(node.template, frame)
+ self.write(', %r).' % self.name)
+ if node.with_context:
+ self.write('make_module(context.parent, True, locals())')
+ else:
+ self.write('module')
+ if frame.toplevel and not node.target.startswith('_'):
+ self.writeline('context.exported_vars.discard(%r)' % node.target)
+ frame.assigned_names.add(node.target)
+
+ def visit_FromImport(self, node, frame):
+ """Visit named imports."""
+ self.newline(node)
+ self.write('included_template = environment.get_template(')
+ self.visit(node.template, frame)
+ self.write(', %r).' % self.name)
+ if node.with_context:
+ self.write('make_module(context.parent, True)')
+ else:
+ self.write('module')
+
+ var_names = []
+ discarded_names = []
+ for name in node.names:
+ if isinstance(name, tuple):
+ name, alias = name
+ else:
+ alias = name
+ self.writeline('l_%s = getattr(included_template, '
+ '%r, missing)' % (alias, name))
+ self.writeline('if l_%s is missing:' % alias)
+ self.indent()
+ self.writeline('l_%s = environment.undefined(%r %% '
+ 'included_template.__name__, '
+ 'name=%r)' %
+ (alias, 'the template %%r (imported on %s) does '
+ 'not export the requested name %s' % (
+ self.position(node),
+ repr(name)
+ ), name))
+ self.outdent()
+ if frame.toplevel:
+ var_names.append(alias)
+ if not alias.startswith('_'):
+ discarded_names.append(alias)
+ frame.assigned_names.add(alias)
+
+ if var_names:
+ if len(var_names) == 1:
+ name = var_names[0]
+ self.writeline('context.vars[%r] = l_%s' % (name, name))
+ else:
+ self.writeline('context.vars.update({%s})' % ', '.join(
+ '%r: l_%s' % (name, name) for name in var_names
+ ))
+ if discarded_names:
+ if len(discarded_names) == 1:
+ self.writeline('context.exported_vars.discard(%r)' %
+ discarded_names[0])
+ else:
+ self.writeline('context.exported_vars.difference_'
+ 'update((%s))' % ', '.join(map(repr, discarded_names)))
+
+ def visit_For(self, node, frame):
+ # when calculating the nodes for the inner frame we have to exclude
+ # the iterator contents from it
+ children = node.iter_child_nodes(exclude=('iter',))
+ if node.recursive:
+ loop_frame = self.function_scoping(node, frame, children,
+ find_special=False)
+ else:
+ loop_frame = frame.inner()
+ loop_frame.inspect(children)
+
+ # try to figure out if we have an extended loop. An extended loop
+ # is necessary if the loop is in recursive mode if the special loop
+ # variable is accessed in the body.
+ extended_loop = node.recursive or 'loop' in \
+ find_undeclared(node.iter_child_nodes(
+ only=('body',)), ('loop',))
+
+ # if we don't have an recursive loop we have to find the shadowed
+ # variables at that point. Because loops can be nested but the loop
+ # variable is a special one we have to enforce aliasing for it.
+ if not node.recursive:
+ aliases = self.push_scope(loop_frame, ('loop',))
+
+ # otherwise we set up a buffer and add a function def
+ else:
+ self.writeline('def loop(reciter, loop_render_func):', node)
+ self.indent()
+ self.buffer(loop_frame)
+ aliases = {}
+
+ # make sure the loop variable is a special one and raise a template
+ # assertion error if a loop tries to write to loop
+ if extended_loop:
+ loop_frame.identifiers.add_special('loop')
+ for name in node.find_all(nodes.Name):
+ if name.ctx == 'store' and name.name == 'loop':
+ self.fail('Can\'t assign to special loop variable '
+ 'in for-loop target', name.lineno)
+
+ self.pull_locals(loop_frame)
+ if node.else_:
+ iteration_indicator = self.temporary_identifier()
+ self.writeline('%s = 1' % iteration_indicator)
+
+ # Create a fake parent loop if the else or test section of a
+ # loop is accessing the special loop variable and no parent loop
+ # exists.
+ if 'loop' not in aliases and 'loop' in find_undeclared(
+ node.iter_child_nodes(only=('else_', 'test')), ('loop',)):
+ self.writeline("l_loop = environment.undefined(%r, name='loop')" %
+ ("'loop' is undefined. the filter section of a loop as well "
+ "as the else block don't have access to the special 'loop'"
+ " variable of the current loop. Because there is no parent "
+ "loop it's undefined. Happened in loop on %s" %
+ self.position(node)))
+
+ self.writeline('for ', node)
+ self.visit(node.target, loop_frame)
+ self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
+
+ # if we have an extened loop and a node test, we filter in the
+ # "outer frame".
+ if extended_loop and node.test is not None:
+ self.write('(')
+ self.visit(node.target, loop_frame)
+ self.write(' for ')
+ self.visit(node.target, loop_frame)
+ self.write(' in ')
+ if node.recursive:
+ self.write('reciter')
+ else:
+ self.visit(node.iter, loop_frame)
+ self.write(' if (')
+ test_frame = loop_frame.copy()
+ self.visit(node.test, test_frame)
+ self.write('))')
+
+ elif node.recursive:
+ self.write('reciter')
+ else:
+ self.visit(node.iter, loop_frame)
+
+ if node.recursive:
+ self.write(', recurse=loop_render_func):')
+ else:
+ self.write(extended_loop and '):' or ':')
+
+ # tests in not extended loops become a continue
+ if not extended_loop and node.test is not None:
+ self.indent()
+ self.writeline('if not ')
+ self.visit(node.test, loop_frame)
+ self.write(':')
+ self.indent()
+ self.writeline('continue')
+ self.outdent(2)
+
+ self.indent()
+ self.blockvisit(node.body, loop_frame)
+ if node.else_:
+ self.writeline('%s = 0' % iteration_indicator)
+ self.outdent()
+
+ if node.else_:
+ self.writeline('if %s:' % iteration_indicator)
+ self.indent()
+ self.blockvisit(node.else_, loop_frame)
+ self.outdent()
+
+ # reset the aliases if there are any.
+ if not node.recursive:
+ self.pop_scope(aliases, loop_frame)
+
+ # if the node was recursive we have to return the buffer contents
+ # and start the iteration code
+ if node.recursive:
+ self.return_buffer_contents(loop_frame)
+ self.outdent()
+ self.start_write(frame, node)
+ self.write('loop(')
+ self.visit(node.iter, frame)
+ self.write(', loop)')
+ self.end_write(frame)
+
+ def visit_If(self, node, frame):
+ if_frame = frame.soft()
+ self.writeline('if ', node)
+ self.visit(node.test, if_frame)
+ self.write(':')
+ self.indent()
+ self.blockvisit(node.body, if_frame)
+ self.outdent()
+ if node.else_:
+ self.writeline('else:')
+ self.indent()
+ self.blockvisit(node.else_, if_frame)
+ self.outdent()
+
+ def visit_Macro(self, node, frame):
+ macro_frame = self.macro_body(node, frame)
+ self.newline()
+ if frame.toplevel:
+ if not node.name.startswith('_'):
+ self.write('context.exported_vars.add(%r)' % node.name)
+ self.writeline('context.vars[%r] = ' % node.name)
+ self.write('l_%s = ' % node.name)
+ self.macro_def(node, macro_frame)
+ frame.assigned_names.add(node.name)
+
+ def visit_CallBlock(self, node, frame):
+ children = node.iter_child_nodes(exclude=('call',))
+ call_frame = self.macro_body(node, frame, children)
+ self.writeline('caller = ')
+ self.macro_def(node, call_frame)
+ self.start_write(frame, node)
+ self.visit_Call(node.call, call_frame, forward_caller=True)
+ self.end_write(frame)
+
+ def visit_FilterBlock(self, node, frame):
+ filter_frame = frame.inner()
+ filter_frame.inspect(node.iter_child_nodes())
+ aliases = self.push_scope(filter_frame)
+ self.pull_locals(filter_frame)
+ self.buffer(filter_frame)
+ self.blockvisit(node.body, filter_frame)
+ self.start_write(frame, node)
+ self.visit_Filter(node.filter, filter_frame)
+ self.end_write(frame)
+ self.pop_scope(aliases, filter_frame)
+
+ def visit_ExprStmt(self, node, frame):
+ self.newline(node)
+ self.visit(node.node, frame)
+
+ def visit_Output(self, node, frame):
+ # if we have a known extends statement, we don't output anything
+ # if we are in a require_output_check section
+ if self.has_known_extends and frame.require_output_check:
+ return
+
+ if self.environment.finalize:
+ finalize = lambda x: unicode(self.environment.finalize(x))
+ else:
+ finalize = unicode
+
+ # if we are inside a frame that requires output checking, we do so
+ outdent_later = False
+ if frame.require_output_check:
+ self.writeline('if parent_template is None:')
+ self.indent()
+ outdent_later = True
+
+ # try to evaluate as many chunks as possible into a static
+ # string at compile time.
+ body = []
+ for child in node.nodes:
+ try:
+ const = child.as_const(frame.eval_ctx)
+ except nodes.Impossible:
+ body.append(child)
+ continue
+ # the frame can't be volatile here, becaus otherwise the
+ # as_const() function would raise an Impossible exception
+ # at that point.
+ try:
+ if frame.eval_ctx.autoescape:
+ if hasattr(const, '__html__'):
+ const = const.__html__()
+ else:
+ const = escape(const)
+ const = finalize(const)
+ except Exception:
+ # if something goes wrong here we evaluate the node
+ # at runtime for easier debugging
+ body.append(child)
+ continue
+ if body and isinstance(body[-1], list):
+ body[-1].append(const)
+ else:
+ body.append([const])
+
+ # if we have less than 3 nodes or a buffer we yield or extend/append
+ if len(body) < 3 or frame.buffer is not None:
+ if frame.buffer is not None:
+ # for one item we append, for more we extend
+ if len(body) == 1:
+ self.writeline('%s.append(' % frame.buffer)
+ else:
+ self.writeline('%s.extend((' % frame.buffer)
+ self.indent()
+ for item in body:
+ if isinstance(item, list):
+ val = repr(concat(item))
+ if frame.buffer is None:
+ self.writeline('yield ' + val)
+ else:
+ self.writeline(val + ', ')
+ else:
+ if frame.buffer is None:
+ self.writeline('yield ', item)
+ else:
+ self.newline(item)
+ close = 1
+ if frame.eval_ctx.volatile:
+ self.write('(context.eval_ctx.autoescape and'
+ ' escape or to_string)(')
+ elif frame.eval_ctx.autoescape:
+ self.write('escape(')
+ else:
+ self.write('to_string(')
+ if self.environment.finalize is not None:
+ self.write('environment.finalize(')
+ close += 1
+ self.visit(item, frame)
+ self.write(')' * close)
+ if frame.buffer is not None:
+ self.write(', ')
+ if frame.buffer is not None:
+ # close the open parentheses
+ self.outdent()
+ self.writeline(len(body) == 1 and ')' or '))')
+
+ # otherwise we create a format string as this is faster in that case
+ else:
+ format = []
+ arguments = []
+ for item in body:
+ if isinstance(item, list):
+ format.append(concat(item).replace('%', '%%'))
+ else:
+ format.append('%s')
+ arguments.append(item)
+ self.writeline('yield ')
+ self.write(repr(concat(format)) + ' % (')
+ idx = -1
+ self.indent()
+ for argument in arguments:
+ self.newline(argument)
+ close = 0
+ if frame.eval_ctx.volatile:
+ self.write('(context.eval_ctx.autoescape and'
+ ' escape or to_string)(')
+ close += 1
+ elif frame.eval_ctx.autoescape:
+ self.write('escape(')
+ close += 1
+ if self.environment.finalize is not None:
+ self.write('environment.finalize(')
+ close += 1
+ self.visit(argument, frame)
+ self.write(')' * close + ', ')
+ self.outdent()
+ self.writeline(')')
+
+ if outdent_later:
+ self.outdent()
+
+ def visit_Assign(self, node, frame):
+ self.newline(node)
+ # toplevel assignments however go into the local namespace and
+ # the current template's context. We create a copy of the frame
+ # here and add a set so that the Name visitor can add the assigned
+ # names here.
+ if frame.toplevel:
+ assignment_frame = frame.copy()
+ assignment_frame.toplevel_assignments = set()
+ else:
+ assignment_frame = frame
+ self.visit(node.target, assignment_frame)
+ self.write(' = ')
+ self.visit(node.node, frame)
+
+ # make sure toplevel assignments are added to the context.
+ if frame.toplevel:
+ public_names = [x for x in assignment_frame.toplevel_assignments
+ if not x.startswith('_')]
+ if len(assignment_frame.toplevel_assignments) == 1:
+ name = next(iter(assignment_frame.toplevel_assignments))
+ self.writeline('context.vars[%r] = l_%s' % (name, name))
+ else:
+ self.writeline('context.vars.update({')
+ for idx, name in enumerate(assignment_frame.toplevel_assignments):
+ if idx:
+ self.write(', ')
+ self.write('%r: l_%s' % (name, name))
+ self.write('})')
+ if public_names:
+ if len(public_names) == 1:
+ self.writeline('context.exported_vars.add(%r)' %
+ public_names[0])
+ else:
+ self.writeline('context.exported_vars.update((%s))' %
+ ', '.join(map(repr, public_names)))
+
+ # -- Expression Visitors
+
+ def visit_Name(self, node, frame):
+ if node.ctx == 'store' and frame.toplevel:
+ frame.toplevel_assignments.add(node.name)
+ self.write('l_' + node.name)
+ frame.assigned_names.add(node.name)
+
+ def visit_Const(self, node, frame):
+ val = node.value
+ if isinstance(val, float):
+ self.write(str(val))
+ else:
+ self.write(repr(val))
+
+ def visit_TemplateData(self, node, frame):
+ try:
+ self.write(repr(node.as_const(frame.eval_ctx)))
+ except nodes.Impossible:
+ self.write('(context.eval_ctx.autoescape and Markup or identity)(%r)'
+ % node.data)
+
+ def visit_Tuple(self, node, frame):
+ self.write('(')
+ idx = -1
+ for idx, item in enumerate(node.items):
+ if idx:
+ self.write(', ')
+ self.visit(item, frame)
+ self.write(idx == 0 and ',)' or ')')
+
+ def visit_List(self, node, frame):
+ self.write('[')
+ for idx, item in enumerate(node.items):
+ if idx:
+ self.write(', ')
+ self.visit(item, frame)
+ self.write(']')
+
+ def visit_Dict(self, node, frame):
+ self.write('{')
+ for idx, item in enumerate(node.items):
+ if idx:
+ self.write(', ')
+ self.visit(item.key, frame)
+ self.write(': ')
+ self.visit(item.value, frame)
+ self.write('}')
+
+ def binop(operator, interceptable=True):
+ def visitor(self, node, frame):
+ if self.environment.sandboxed and \
+ operator in self.environment.intercepted_binops:
+ self.write('environment.call_binop(context, %r, ' % operator)
+ self.visit(node.left, frame)
+ self.write(', ')
+ self.visit(node.right, frame)
+ else:
+ self.write('(')
+ self.visit(node.left, frame)
+ self.write(' %s ' % operator)
+ self.visit(node.right, frame)
+ self.write(')')
+ return visitor
+
+ def uaop(operator, interceptable=True):
+ def visitor(self, node, frame):
+ if self.environment.sandboxed and \
+ operator in self.environment.intercepted_unops:
+ self.write('environment.call_unop(context, %r, ' % operator)
+ self.visit(node.node, frame)
+ else:
+ self.write('(' + operator)
+ self.visit(node.node, frame)
+ self.write(')')
+ return visitor
+
+ visit_Add = binop('+')
+ visit_Sub = binop('-')
+ visit_Mul = binop('*')
+ visit_Div = binop('/')
+ visit_FloorDiv = binop('//')
+ visit_Pow = binop('**')
+ visit_Mod = binop('%')
+ visit_And = binop('and', interceptable=False)
+ visit_Or = binop('or', interceptable=False)
+ visit_Pos = uaop('+')
+ visit_Neg = uaop('-')
+ visit_Not = uaop('not ', interceptable=False)
+ del binop, uaop
+
+ def visit_Concat(self, node, frame):
+ if frame.eval_ctx.volatile:
+ func_name = '(context.eval_ctx.volatile and' \
+ ' markup_join or unicode_join)'
+ elif frame.eval_ctx.autoescape:
+ func_name = 'markup_join'
+ else:
+ func_name = 'unicode_join'
+ self.write('%s((' % func_name)
+ for arg in node.nodes:
+ self.visit(arg, frame)
+ self.write(', ')
+ self.write('))')
+
+ def visit_Compare(self, node, frame):
+ self.visit(node.expr, frame)
+ for op in node.ops:
+ self.visit(op, frame)
+
+ def visit_Operand(self, node, frame):
+ self.write(' %s ' % operators[node.op])
+ self.visit(node.expr, frame)
+
+ def visit_Getattr(self, node, frame):
+ self.write('environment.getattr(')
+ self.visit(node.node, frame)
+ self.write(', %r)' % node.attr)
+
+ def visit_Getitem(self, node, frame):
+ # slices bypass the environment getitem method.
+ if isinstance(node.arg, nodes.Slice):
+ self.visit(node.node, frame)
+ self.write('[')
+ self.visit(node.arg, frame)
+ self.write(']')
+ else:
+ self.write('environment.getitem(')
+ self.visit(node.node, frame)
+ self.write(', ')
+ self.visit(node.arg, frame)
+ self.write(')')
+
+ def visit_Slice(self, node, frame):
+ if node.start is not None:
+ self.visit(node.start, frame)
+ self.write(':')
+ if node.stop is not None:
+ self.visit(node.stop, frame)
+ if node.step is not None:
+ self.write(':')
+ self.visit(node.step, frame)
+
+ def visit_Filter(self, node, frame):
+ self.write(self.filters[node.name] + '(')
+ func = self.environment.filters.get(node.name)
+ if func is None:
+ self.fail('no filter named %r' % node.name, node.lineno)
+ if getattr(func, 'contextfilter', False):
+ self.write('context, ')
+ elif getattr(func, 'evalcontextfilter', False):
+ self.write('context.eval_ctx, ')
+ elif getattr(func, 'environmentfilter', False):
+ self.write('environment, ')
+
+ # if the filter node is None we are inside a filter block
+ # and want to write to the current buffer
+ if node.node is not None:
+ self.visit(node.node, frame)
+ elif frame.eval_ctx.volatile:
+ self.write('(context.eval_ctx.autoescape and'
+ ' Markup(concat(%s)) or concat(%s))' %
+ (frame.buffer, frame.buffer))
+ elif frame.eval_ctx.autoescape:
+ self.write('Markup(concat(%s))' % frame.buffer)
+ else:
+ self.write('concat(%s)' % frame.buffer)
+ self.signature(node, frame)
+ self.write(')')
+
+ def visit_Test(self, node, frame):
+ self.write(self.tests[node.name] + '(')
+ if node.name not in self.environment.tests:
+ self.fail('no test named %r' % node.name, node.lineno)
+ self.visit(node.node, frame)
+ self.signature(node, frame)
+ self.write(')')
+
+ def visit_CondExpr(self, node, frame):
+ def write_expr2():
+ if node.expr2 is not None:
+ return self.visit(node.expr2, frame)
+ self.write('environment.undefined(%r)' % ('the inline if-'
+ 'expression on %s evaluated to false and '
+ 'no else section was defined.' % self.position(node)))
+
+ if not have_condexpr:
+ self.write('((')
+ self.visit(node.test, frame)
+ self.write(') and (')
+ self.visit(node.expr1, frame)
+ self.write(',) or (')
+ write_expr2()
+ self.write(',))[0]')
+ else:
+ self.write('(')
+ self.visit(node.expr1, frame)
+ self.write(' if ')
+ self.visit(node.test, frame)
+ self.write(' else ')
+ write_expr2()
+ self.write(')')
+
+ def visit_Call(self, node, frame, forward_caller=False):
+ if self.environment.sandboxed:
+ self.write('environment.call(context, ')
+ else:
+ self.write('context.call(')
+ self.visit(node.node, frame)
+ extra_kwargs = forward_caller and {'caller': 'caller'} or None
+ self.signature(node, frame, extra_kwargs)
+ self.write(')')
+
+ def visit_Keyword(self, node, frame):
+ self.write(node.key + '=')
+ self.visit(node.value, frame)
+
+ # -- Unused nodes for extensions
+
+ def visit_MarkSafe(self, node, frame):
+ self.write('Markup(')
+ self.visit(node.expr, frame)
+ self.write(')')
+
+ def visit_MarkSafeIfAutoescape(self, node, frame):
+ self.write('(context.eval_ctx.autoescape and Markup or identity)(')
+ self.visit(node.expr, frame)
+ self.write(')')
+
+ def visit_EnvironmentAttribute(self, node, frame):
+ self.write('environment.' + node.name)
+
+ def visit_ExtensionAttribute(self, node, frame):
+ self.write('environment.extensions[%r].%s' % (node.identifier, node.name))
+
+ def visit_ImportedName(self, node, frame):
+ self.write(self.import_aliases[node.importname])
+
+ def visit_InternalName(self, node, frame):
+ self.write(node.name)
+
+ def visit_ContextReference(self, node, frame):
+ self.write('context')
+
+ def visit_Continue(self, node, frame):
+ self.writeline('continue', node)
+
+ def visit_Break(self, node, frame):
+ self.writeline('break', node)
+
+ def visit_Scope(self, node, frame):
+ scope_frame = frame.inner()
+ scope_frame.inspect(node.iter_child_nodes())
+ aliases = self.push_scope(scope_frame)
+ self.pull_locals(scope_frame)
+ self.blockvisit(node.body, scope_frame)
+ self.pop_scope(aliases, scope_frame)
+
+ def visit_EvalContextModifier(self, node, frame):
+ for keyword in node.options:
+ self.writeline('context.eval_ctx.%s = ' % keyword.key)
+ self.visit(keyword.value, frame)
+ try:
+ val = keyword.value.as_const(frame.eval_ctx)
+ except nodes.Impossible:
+ frame.eval_ctx.volatile = True
+ else:
+ setattr(frame.eval_ctx, keyword.key, val)
+
+ def visit_ScopedEvalContextModifier(self, node, frame):
+ old_ctx_name = self.temporary_identifier()
+ safed_ctx = frame.eval_ctx.save()
+ self.writeline('%s = context.eval_ctx.save()' % old_ctx_name)
+ self.visit_EvalContextModifier(node, frame)
+ for child in node.body:
+ self.visit(child, frame)
+ frame.eval_ctx.revert(safed_ctx)
+ self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name)
diff --git a/websdk/jinja2/constants.py b/websdk/jinja2/constants.py
new file mode 100644
index 0000000..cab203c
--- /dev/null
+++ b/websdk/jinja2/constants.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja.constants
+ ~~~~~~~~~~~~~~~
+
+ Various constants.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+
+#: list of lorem ipsum words used by the lipsum() helper function
+LOREM_IPSUM_WORDS = u'''\
+a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at
+auctor augue bibendum blandit class commodo condimentum congue consectetuer
+consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus
+diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend
+elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames
+faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac
+hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum
+justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem
+luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie
+mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non
+nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque
+penatibus per pharetra phasellus placerat platea porta porttitor posuere
+potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus
+ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit
+sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor
+tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices
+ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus
+viverra volutpat vulputate'''
diff --git a/websdk/jinja2/debug.py b/websdk/jinja2/debug.py
new file mode 100644
index 0000000..3ac4041
--- /dev/null
+++ b/websdk/jinja2/debug.py
@@ -0,0 +1,339 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.debug
+ ~~~~~~~~~~~~
+
+ Implements the debug interface for Jinja. This module does some pretty
+ ugly stuff with the Python traceback system in order to achieve tracebacks
+ with correct line numbers, locals and contents.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+import traceback
+from types import TracebackType
+from jinja2.utils import CodeType, missing, internal_code
+from jinja2.exceptions import TemplateSyntaxError
+
+# on pypy we can take advantage of transparent proxies
+try:
+ from __pypy__ import tproxy
+except ImportError:
+ tproxy = None
+
+
+# how does the raise helper look like?
+try:
+ exec "raise TypeError, 'foo'"
+except SyntaxError:
+ raise_helper = 'raise __jinja_exception__[1]'
+except TypeError:
+ raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]'
+
+
+class TracebackFrameProxy(object):
+ """Proxies a traceback frame."""
+
+ def __init__(self, tb):
+ self.tb = tb
+ self._tb_next = None
+
+ @property
+ def tb_next(self):
+ return self._tb_next
+
+ def set_next(self, next):
+ if tb_set_next is not None:
+ try:
+ tb_set_next(self.tb, next and next.tb or None)
+ except Exception:
+ # this function can fail due to all the hackery it does
+ # on various python implementations. We just catch errors
+ # down and ignore them if necessary.
+ pass
+ self._tb_next = next
+
+ @property
+ def is_jinja_frame(self):
+ return '__jinja_template__' in self.tb.tb_frame.f_globals
+
+ def __getattr__(self, name):
+ return getattr(self.tb, name)
+
+
+def make_frame_proxy(frame):
+ proxy = TracebackFrameProxy(frame)
+ if tproxy is None:
+ return proxy
+ def operation_handler(operation, *args, **kwargs):
+ if operation in ('__getattribute__', '__getattr__'):
+ return getattr(proxy, args[0])
+ elif operation == '__setattr__':
+ proxy.__setattr__(*args, **kwargs)
+ else:
+ return getattr(proxy, operation)(*args, **kwargs)
+ return tproxy(TracebackType, operation_handler)
+
+
+class ProcessedTraceback(object):
+ """Holds a Jinja preprocessed traceback for printing or reraising."""
+
+ def __init__(self, exc_type, exc_value, frames):
+ assert frames, 'no frames for this traceback?'
+ self.exc_type = exc_type
+ self.exc_value = exc_value
+ self.frames = frames
+
+ # newly concatenate the frames (which are proxies)
+ prev_tb = None
+ for tb in self.frames:
+ if prev_tb is not None:
+ prev_tb.set_next(tb)
+ prev_tb = tb
+ prev_tb.set_next(None)
+
+ def render_as_text(self, limit=None):
+ """Return a string with the traceback."""
+ lines = traceback.format_exception(self.exc_type, self.exc_value,
+ self.frames[0], limit=limit)
+ return ''.join(lines).rstrip()
+
+ def render_as_html(self, full=False):
+ """Return a unicode string with the traceback as rendered HTML."""
+ from jinja2.debugrenderer import render_traceback
+ return u'%s\n\n<!--\n%s\n-->' % (
+ render_traceback(self, full=full),
+ self.render_as_text().decode('utf-8', 'replace')
+ )
+
+ @property
+ def is_template_syntax_error(self):
+ """`True` if this is a template syntax error."""
+ return isinstance(self.exc_value, TemplateSyntaxError)
+
+ @property
+ def exc_info(self):
+ """Exception info tuple with a proxy around the frame objects."""
+ return self.exc_type, self.exc_value, self.frames[0]
+
+ @property
+ def standard_exc_info(self):
+ """Standard python exc_info for re-raising"""
+ tb = self.frames[0]
+ # the frame will be an actual traceback (or transparent proxy) if
+ # we are on pypy or a python implementation with support for tproxy
+ if type(tb) is not TracebackType:
+ tb = tb.tb
+ return self.exc_type, self.exc_value, tb
+
+
+def make_traceback(exc_info, source_hint=None):
+ """Creates a processed traceback object from the exc_info."""
+ exc_type, exc_value, tb = exc_info
+ if isinstance(exc_value, TemplateSyntaxError):
+ exc_info = translate_syntax_error(exc_value, source_hint)
+ initial_skip = 0
+ else:
+ initial_skip = 1
+ return translate_exception(exc_info, initial_skip)
+
+
+def translate_syntax_error(error, source=None):
+ """Rewrites a syntax error to please traceback systems."""
+ error.source = source
+ error.translated = True
+ exc_info = (error.__class__, error, None)
+ filename = error.filename
+ if filename is None:
+ filename = '<unknown>'
+ return fake_exc_info(exc_info, filename, error.lineno)
+
+
+def translate_exception(exc_info, initial_skip=0):
+ """If passed an exc_info it will automatically rewrite the exceptions
+ all the way down to the correct line numbers and frames.
+ """
+ tb = exc_info[2]
+ frames = []
+
+ # skip some internal frames if wanted
+ for x in xrange(initial_skip):
+ if tb is not None:
+ tb = tb.tb_next
+ initial_tb = tb
+
+ while tb is not None:
+ # skip frames decorated with @internalcode. These are internal
+ # calls we can't avoid and that are useless in template debugging
+ # output.
+ if tb.tb_frame.f_code in internal_code:
+ tb = tb.tb_next
+ continue
+
+ # save a reference to the next frame if we override the current
+ # one with a faked one.
+ next = tb.tb_next
+
+ # fake template exceptions
+ template = tb.tb_frame.f_globals.get('__jinja_template__')
+ if template is not None:
+ lineno = template.get_corresponding_lineno(tb.tb_lineno)
+ tb = fake_exc_info(exc_info[:2] + (tb,), template.filename,
+ lineno)[2]
+
+ frames.append(make_frame_proxy(tb))
+ tb = next
+
+ # if we don't have any exceptions in the frames left, we have to
+ # reraise it unchanged.
+ # XXX: can we backup here? when could this happen?
+ if not frames:
+ raise exc_info[0], exc_info[1], exc_info[2]
+
+ return ProcessedTraceback(exc_info[0], exc_info[1], frames)
+
+
+def fake_exc_info(exc_info, filename, lineno):
+ """Helper for `translate_exception`."""
+ exc_type, exc_value, tb = exc_info
+
+ # figure the real context out
+ if tb is not None:
+ real_locals = tb.tb_frame.f_locals.copy()
+ ctx = real_locals.get('context')
+ if ctx:
+ locals = ctx.get_all()
+ else:
+ locals = {}
+ for name, value in real_locals.iteritems():
+ if name.startswith('l_') and value is not missing:
+ locals[name[2:]] = value
+
+ # if there is a local called __jinja_exception__, we get
+ # rid of it to not break the debug functionality.
+ locals.pop('__jinja_exception__', None)
+ else:
+ locals = {}
+
+ # assamble fake globals we need
+ globals = {
+ '__name__': filename,
+ '__file__': filename,
+ '__jinja_exception__': exc_info[:2],
+
+ # we don't want to keep the reference to the template around
+ # to not cause circular dependencies, but we mark it as Jinja
+ # frame for the ProcessedTraceback
+ '__jinja_template__': None
+ }
+
+ # and fake the exception
+ code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec')
+
+ # if it's possible, change the name of the code. This won't work
+ # on some python environments such as google appengine
+ try:
+ if tb is None:
+ location = 'template'
+ else:
+ function = tb.tb_frame.f_code.co_name
+ if function == 'root':
+ location = 'top-level template code'
+ elif function.startswith('block_'):
+ location = 'block "%s"' % function[6:]
+ else:
+ location = 'template'
+ code = CodeType(0, code.co_nlocals, code.co_stacksize,
+ code.co_flags, code.co_code, code.co_consts,
+ code.co_names, code.co_varnames, filename,
+ location, code.co_firstlineno,
+ code.co_lnotab, (), ())
+ except:
+ pass
+
+ # execute the code and catch the new traceback
+ try:
+ exec code in globals, locals
+ except:
+ exc_info = sys.exc_info()
+ new_tb = exc_info[2].tb_next
+
+ # return without this frame
+ return exc_info[:2] + (new_tb,)
+
+
+def _init_ugly_crap():
+ """This function implements a few ugly things so that we can patch the
+ traceback objects. The function returned allows resetting `tb_next` on
+ any python traceback object. Do not attempt to use this on non cpython
+ interpreters
+ """
+ import ctypes
+ from types import TracebackType
+
+ # figure out side of _Py_ssize_t
+ if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
+ _Py_ssize_t = ctypes.c_int64
+ else:
+ _Py_ssize_t = ctypes.c_int
+
+ # regular python
+ class _PyObject(ctypes.Structure):
+ pass
+ _PyObject._fields_ = [
+ ('ob_refcnt', _Py_ssize_t),
+ ('ob_type', ctypes.POINTER(_PyObject))
+ ]
+
+ # python with trace
+ if hasattr(sys, 'getobjects'):
+ class _PyObject(ctypes.Structure):
+ pass
+ _PyObject._fields_ = [
+ ('_ob_next', ctypes.POINTER(_PyObject)),
+ ('_ob_prev', ctypes.POINTER(_PyObject)),
+ ('ob_refcnt', _Py_ssize_t),
+ ('ob_type', ctypes.POINTER(_PyObject))
+ ]
+
+ class _Traceback(_PyObject):
+ pass
+ _Traceback._fields_ = [
+ ('tb_next', ctypes.POINTER(_Traceback)),
+ ('tb_frame', ctypes.POINTER(_PyObject)),
+ ('tb_lasti', ctypes.c_int),
+ ('tb_lineno', ctypes.c_int)
+ ]
+
+ def tb_set_next(tb, next):
+ """Set the tb_next attribute of a traceback object."""
+ if not (isinstance(tb, TracebackType) and
+ (next is None or isinstance(next, TracebackType))):
+ raise TypeError('tb_set_next arguments must be traceback objects')
+ obj = _Traceback.from_address(id(tb))
+ if tb.tb_next is not None:
+ old = _Traceback.from_address(id(tb.tb_next))
+ old.ob_refcnt -= 1
+ if next is None:
+ obj.tb_next = ctypes.POINTER(_Traceback)()
+ else:
+ next = _Traceback.from_address(id(next))
+ next.ob_refcnt += 1
+ obj.tb_next = ctypes.pointer(next)
+
+ return tb_set_next
+
+
+# try to get a tb_set_next implementation if we don't have transparent
+# proxies.
+tb_set_next = None
+if tproxy is None:
+ try:
+ from jinja2._debugsupport import tb_set_next
+ except ImportError:
+ try:
+ tb_set_next = _init_ugly_crap()
+ except:
+ pass
+ del _init_ugly_crap
diff --git a/websdk/jinja2/defaults.py b/websdk/jinja2/defaults.py
new file mode 100644
index 0000000..d2d4544
--- /dev/null
+++ b/websdk/jinja2/defaults.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.defaults
+ ~~~~~~~~~~~~~~~
+
+ Jinja default filters and tags.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner
+
+
+# defaults for the parser / lexer
+BLOCK_START_STRING = '{%'
+BLOCK_END_STRING = '%}'
+VARIABLE_START_STRING = '{{'
+VARIABLE_END_STRING = '}}'
+COMMENT_START_STRING = '{#'
+COMMENT_END_STRING = '#}'
+LINE_STATEMENT_PREFIX = None
+LINE_COMMENT_PREFIX = None
+TRIM_BLOCKS = False
+NEWLINE_SEQUENCE = '\n'
+
+
+# default filters, tests and namespace
+from jinja2.filters import FILTERS as DEFAULT_FILTERS
+from jinja2.tests import TESTS as DEFAULT_TESTS
+DEFAULT_NAMESPACE = {
+ 'range': xrange,
+ 'dict': lambda **kw: kw,
+ 'lipsum': generate_lorem_ipsum,
+ 'cycler': Cycler,
+ 'joiner': Joiner
+}
+
+
+# export all constants
+__all__ = tuple(x for x in locals().keys() if x.isupper())
diff --git a/websdk/jinja2/environment.py b/websdk/jinja2/environment.py
new file mode 100644
index 0000000..ebb5454
--- /dev/null
+++ b/websdk/jinja2/environment.py
@@ -0,0 +1,1121 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.environment
+ ~~~~~~~~~~~~~~~~~~
+
+ Provides a class that holds runtime and parsing time options.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+from jinja2 import nodes
+from jinja2.defaults import *
+from jinja2.lexer import get_lexer, TokenStream
+from jinja2.parser import Parser
+from jinja2.optimizer import optimize
+from jinja2.compiler import generate
+from jinja2.runtime import Undefined, new_context
+from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \
+ TemplatesNotFound
+from jinja2.utils import import_string, LRUCache, Markup, missing, \
+ concat, consume, internalcode, _encode_filename
+
+
+# for direct template usage we have up to ten living environments
+_spontaneous_environments = LRUCache(10)
+
+# the function to create jinja traceback objects. This is dynamically
+# imported on the first exception in the exception handler.
+_make_traceback = None
+
+
+def get_spontaneous_environment(*args):
+ """Return a new spontaneous environment. A spontaneous environment is an
+ unnamed and unaccessible (in theory) environment that is used for
+ templates generated from a string and not from the file system.
+ """
+ try:
+ env = _spontaneous_environments.get(args)
+ except TypeError:
+ return Environment(*args)
+ if env is not None:
+ return env
+ _spontaneous_environments[args] = env = Environment(*args)
+ env.shared = True
+ return env
+
+
+def create_cache(size):
+ """Return the cache class for the given size."""
+ if size == 0:
+ return None
+ if size < 0:
+ return {}
+ return LRUCache(size)
+
+
+def copy_cache(cache):
+ """Create an empty copy of the given cache."""
+ if cache is None:
+ return None
+ elif type(cache) is dict:
+ return {}
+ return LRUCache(cache.capacity)
+
+
+def load_extensions(environment, extensions):
+ """Load the extensions from the list and bind it to the environment.
+ Returns a dict of instantiated environments.
+ """
+ result = {}
+ for extension in extensions:
+ if isinstance(extension, basestring):
+ extension = import_string(extension)
+ result[extension.identifier] = extension(environment)
+ return result
+
+
+def _environment_sanity_check(environment):
+ """Perform a sanity check on the environment."""
+ assert issubclass(environment.undefined, Undefined), 'undefined must ' \
+ 'be a subclass of undefined because filters depend on it.'
+ assert environment.block_start_string != \
+ environment.variable_start_string != \
+ environment.comment_start_string, 'block, variable and comment ' \
+ 'start strings must be different'
+ assert environment.newline_sequence in ('\r', '\r\n', '\n'), \
+ 'newline_sequence set to unknown line ending string.'
+ return environment
+
+
+class Environment(object):
+ r"""The core component of Jinja is the `Environment`. It contains
+ important shared variables like configuration, filters, tests,
+ globals and others. Instances of this class may be modified if
+ they are not shared and if no template was loaded so far.
+ Modifications on environments after the first template was loaded
+ will lead to surprising effects and undefined behavior.
+
+ Here the possible initialization parameters:
+
+ `block_start_string`
+ The string marking the begin of a block. Defaults to ``'{%'``.
+
+ `block_end_string`
+ The string marking the end of a block. Defaults to ``'%}'``.
+
+ `variable_start_string`
+ The string marking the begin of a print statement.
+ Defaults to ``'{{'``.
+
+ `variable_end_string`
+ The string marking the end of a print statement. Defaults to
+ ``'}}'``.
+
+ `comment_start_string`
+ The string marking the begin of a comment. Defaults to ``'{#'``.
+
+ `comment_end_string`
+ The string marking the end of a comment. Defaults to ``'#}'``.
+
+ `line_statement_prefix`
+ If given and a string, this will be used as prefix for line based
+ statements. See also :ref:`line-statements`.
+
+ `line_comment_prefix`
+ If given and a string, this will be used as prefix for line based
+ based comments. See also :ref:`line-statements`.
+
+ .. versionadded:: 2.2
+
+ `trim_blocks`
+ If this is set to ``True`` the first newline after a block is
+ removed (block, not variable tag!). Defaults to `False`.
+
+ `newline_sequence`
+ The sequence that starts a newline. Must be one of ``'\r'``,
+ ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a
+ useful default for Linux and OS X systems as well as web
+ applications.
+
+ `extensions`
+ List of Jinja extensions to use. This can either be import paths
+ as strings or extension classes. For more information have a
+ look at :ref:`the extensions documentation <jinja-extensions>`.
+
+ `optimized`
+ should the optimizer be enabled? Default is `True`.
+
+ `undefined`
+ :class:`Undefined` or a subclass of it that is used to represent
+ undefined values in the template.
+
+ `finalize`
+ A callable that can be used to process the result of a variable
+ expression before it is output. For example one can convert
+ `None` implicitly into an empty string here.
+
+ `autoescape`
+ If set to true the XML/HTML autoescaping feature is enabled by
+ default. For more details about auto escaping see
+ :class:`~jinja2.utils.Markup`. As of Jinja 2.4 this can also
+ be a callable that is passed the template name and has to
+ return `True` or `False` depending on autoescape should be
+ enabled by default.
+
+ .. versionchanged:: 2.4
+ `autoescape` can now be a function
+
+ `loader`
+ The template loader for this environment.
+
+ `cache_size`
+ The size of the cache. Per default this is ``50`` which means
+ that if more than 50 templates are loaded the loader will clean
+ out the least recently used template. If the cache size is set to
+ ``0`` templates are recompiled all the time, if the cache size is
+ ``-1`` the cache will not be cleaned.
+
+ `auto_reload`
+ Some loaders load templates from locations where the template
+ sources may change (ie: file system or database). If
+ `auto_reload` is set to `True` (default) every time a template is
+ requested the loader checks if the source changed and if yes, it
+ will reload the template. For higher performance it's possible to
+ disable that.
+
+ `bytecode_cache`
+ If set to a bytecode cache object, this object will provide a
+ cache for the internal Jinja bytecode so that templates don't
+ have to be parsed if they were not changed.
+
+ See :ref:`bytecode-cache` for more information.
+ """
+
+ #: if this environment is sandboxed. Modifying this variable won't make
+ #: the environment sandboxed though. For a real sandboxed environment
+ #: have a look at jinja2.sandbox. This flag alone controls the code
+ #: generation by the compiler.
+ sandboxed = False
+
+ #: True if the environment is just an overlay
+ overlayed = False
+
+ #: the environment this environment is linked to if it is an overlay
+ linked_to = None
+
+ #: shared environments have this set to `True`. A shared environment
+ #: must not be modified
+ shared = False
+
+ #: these are currently EXPERIMENTAL undocumented features.
+ exception_handler = None
+ exception_formatter = None
+
+ def __init__(self,
+ block_start_string=BLOCK_START_STRING,
+ block_end_string=BLOCK_END_STRING,
+ variable_start_string=VARIABLE_START_STRING,
+ variable_end_string=VARIABLE_END_STRING,
+ comment_start_string=COMMENT_START_STRING,
+ comment_end_string=COMMENT_END_STRING,
+ line_statement_prefix=LINE_STATEMENT_PREFIX,
+ line_comment_prefix=LINE_COMMENT_PREFIX,
+ trim_blocks=TRIM_BLOCKS,
+ newline_sequence=NEWLINE_SEQUENCE,
+ extensions=(),
+ optimized=True,
+ undefined=Undefined,
+ finalize=None,
+ autoescape=False,
+ loader=None,
+ cache_size=50,
+ auto_reload=True,
+ bytecode_cache=None):
+ # !!Important notice!!
+ # The constructor accepts quite a few arguments that should be
+ # passed by keyword rather than position. However it's important to
+ # not change the order of arguments because it's used at least
+ # internally in those cases:
+ # - spontaneous environments (i18n extension and Template)
+ # - unittests
+ # If parameter changes are required only add parameters at the end
+ # and don't change the arguments (or the defaults!) of the arguments
+ # existing already.
+
+ # lexer / parser information
+ self.block_start_string = block_start_string
+ self.block_end_string = block_end_string
+ self.variable_start_string = variable_start_string
+ self.variable_end_string = variable_end_string
+ self.comment_start_string = comment_start_string
+ self.comment_end_string = comment_end_string
+ self.line_statement_prefix = line_statement_prefix
+ self.line_comment_prefix = line_comment_prefix
+ self.trim_blocks = trim_blocks
+ self.newline_sequence = newline_sequence
+
+ # runtime information
+ self.undefined = undefined
+ self.optimized = optimized
+ self.finalize = finalize
+ self.autoescape = autoescape
+
+ # defaults
+ self.filters = DEFAULT_FILTERS.copy()
+ self.tests = DEFAULT_TESTS.copy()
+ self.globals = DEFAULT_NAMESPACE.copy()
+
+ # set the loader provided
+ self.loader = loader
+ self.bytecode_cache = None
+ self.cache = create_cache(cache_size)
+ self.bytecode_cache = bytecode_cache
+ self.auto_reload = auto_reload
+
+ # load extensions
+ self.extensions = load_extensions(self, extensions)
+
+ _environment_sanity_check(self)
+
+ def add_extension(self, extension):
+ """Adds an extension after the environment was created.
+
+ .. versionadded:: 2.5
+ """
+ self.extensions.update(load_extensions(self, [extension]))
+
+ def extend(self, **attributes):
+ """Add the items to the instance of the environment if they do not exist
+ yet. This is used by :ref:`extensions <writing-extensions>` to register
+ callbacks and configuration values without breaking inheritance.
+ """
+ for key, value in attributes.iteritems():
+ if not hasattr(self, key):
+ setattr(self, key, value)
+
+ def overlay(self, block_start_string=missing, block_end_string=missing,
+ variable_start_string=missing, variable_end_string=missing,
+ comment_start_string=missing, comment_end_string=missing,
+ line_statement_prefix=missing, line_comment_prefix=missing,
+ trim_blocks=missing, extensions=missing, optimized=missing,
+ undefined=missing, finalize=missing, autoescape=missing,
+ loader=missing, cache_size=missing, auto_reload=missing,
+ bytecode_cache=missing):
+ """Create a new overlay environment that shares all the data with the
+ current environment except of cache and the overridden attributes.
+ Extensions cannot be removed for an overlayed environment. An overlayed
+ environment automatically gets all the extensions of the environment it
+ is linked to plus optional extra extensions.
+
+ Creating overlays should happen after the initial environment was set
+ up completely. Not all attributes are truly linked, some are just
+ copied over so modifications on the original environment may not shine
+ through.
+ """
+ args = dict(locals())
+ del args['self'], args['cache_size'], args['extensions']
+
+ rv = object.__new__(self.__class__)
+ rv.__dict__.update(self.__dict__)
+ rv.overlayed = True
+ rv.linked_to = self
+
+ for key, value in args.iteritems():
+ if value is not missing:
+ setattr(rv, key, value)
+
+ if cache_size is not missing:
+ rv.cache = create_cache(cache_size)
+ else:
+ rv.cache = copy_cache(self.cache)
+
+ rv.extensions = {}
+ for key, value in self.extensions.iteritems():
+ rv.extensions[key] = value.bind(rv)
+ if extensions is not missing:
+ rv.extensions.update(load_extensions(rv, extensions))
+
+ return _environment_sanity_check(rv)
+
+ lexer = property(get_lexer, doc="The lexer for this environment.")
+
+ def iter_extensions(self):
+ """Iterates over the extensions by priority."""
+ return iter(sorted(self.extensions.values(),
+ key=lambda x: x.priority))
+
+ def getitem(self, obj, argument):
+ """Get an item or attribute of an object but prefer the item."""
+ try:
+ return obj[argument]
+ except (TypeError, LookupError):
+ if isinstance(argument, basestring):
+ try:
+ attr = str(argument)
+ except Exception:
+ pass
+ else:
+ try:
+ return getattr(obj, attr)
+ except AttributeError:
+ pass
+ return self.undefined(obj=obj, name=argument)
+
+ def getattr(self, obj, attribute):
+ """Get an item or attribute of an object but prefer the attribute.
+ Unlike :meth:`getitem` the attribute *must* be a bytestring.
+ """
+ try:
+ return getattr(obj, attribute)
+ except AttributeError:
+ pass
+ try:
+ return obj[attribute]
+ except (TypeError, LookupError, AttributeError):
+ return self.undefined(obj=obj, name=attribute)
+
+ @internalcode
+ def parse(self, source, name=None, filename=None):
+ """Parse the sourcecode and return the abstract syntax tree. This
+ tree of nodes is used by the compiler to convert the template into
+ executable source- or bytecode. This is useful for debugging or to
+ extract information from templates.
+
+ If you are :ref:`developing Jinja2 extensions <writing-extensions>`
+ this gives you a good overview of the node tree generated.
+ """
+ try:
+ return self._parse(source, name, filename)
+ except TemplateSyntaxError:
+ exc_info = sys.exc_info()
+ self.handle_exception(exc_info, source_hint=source)
+
+ def _parse(self, source, name, filename):
+ """Internal parsing function used by `parse` and `compile`."""
+ return Parser(self, source, name, _encode_filename(filename)).parse()
+
+ def lex(self, source, name=None, filename=None):
+ """Lex the given sourcecode and return a generator that yields
+ tokens as tuples in the form ``(lineno, token_type, value)``.
+ This can be useful for :ref:`extension development <writing-extensions>`
+ and debugging templates.
+
+ This does not perform preprocessing. If you want the preprocessing
+ of the extensions to be applied you have to filter source through
+ the :meth:`preprocess` method.
+ """
+ source = unicode(source)
+ try:
+ return self.lexer.tokeniter(source, name, filename)
+ except TemplateSyntaxError:
+ exc_info = sys.exc_info()
+ self.handle_exception(exc_info, source_hint=source)
+
+ def preprocess(self, source, name=None, filename=None):
+ """Preprocesses the source with all extensions. This is automatically
+ called for all parsing and compiling methods but *not* for :meth:`lex`
+ because there you usually only want the actual source tokenized.
+ """
+ return reduce(lambda s, e: e.preprocess(s, name, filename),
+ self.iter_extensions(), unicode(source))
+
+ def _tokenize(self, source, name, filename=None, state=None):
+ """Called by the parser to do the preprocessing and filtering
+ for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`.
+ """
+ source = self.preprocess(source, name, filename)
+ stream = self.lexer.tokenize(source, name, filename, state)
+ for ext in self.iter_extensions():
+ stream = ext.filter_stream(stream)
+ if not isinstance(stream, TokenStream):
+ stream = TokenStream(stream, name, filename)
+ return stream
+
+ def _generate(self, source, name, filename, defer_init=False):
+ """Internal hook that can be overridden to hook a different generate
+ method in.
+
+ .. versionadded:: 2.5
+ """
+ return generate(source, self, name, filename, defer_init=defer_init)
+
+ def _compile(self, source, filename):
+ """Internal hook that can be overridden to hook a different compile
+ method in.
+
+ .. versionadded:: 2.5
+ """
+ return compile(source, filename, 'exec')
+
+ @internalcode
+ def compile(self, source, name=None, filename=None, raw=False,
+ defer_init=False):
+ """Compile a node or template source code. The `name` parameter is
+ the load name of the template after it was joined using
+ :meth:`join_path` if necessary, not the filename on the file system.
+ the `filename` parameter is the estimated filename of the template on
+ the file system. If the template came from a database or memory this
+ can be omitted.
+
+ The return value of this method is a python code object. If the `raw`
+ parameter is `True` the return value will be a string with python
+ code equivalent to the bytecode returned otherwise. This method is
+ mainly used internally.
+
+ `defer_init` is use internally to aid the module code generator. This
+ causes the generated code to be able to import without the global
+ environment variable to be set.
+
+ .. versionadded:: 2.4
+ `defer_init` parameter added.
+ """
+ source_hint = None
+ try:
+ if isinstance(source, basestring):
+ source_hint = source
+ source = self._parse(source, name, filename)
+ if self.optimized:
+ source = optimize(source, self)
+ source = self._generate(source, name, filename,
+ defer_init=defer_init)
+ if raw:
+ return source
+ if filename is None:
+ filename = '<template>'
+ else:
+ filename = _encode_filename(filename)
+ return self._compile(source, filename)
+ except TemplateSyntaxError:
+ exc_info = sys.exc_info()
+ self.handle_exception(exc_info, source_hint=source)
+
+ def compile_expression(self, source, undefined_to_none=True):
+ """A handy helper method that returns a callable that accepts keyword
+ arguments that appear as variables in the expression. If called it
+ returns the result of the expression.
+
+ This is useful if applications want to use the same rules as Jinja
+ in template "configuration files" or similar situations.
+
+ Example usage:
+
+ >>> env = Environment()
+ >>> expr = env.compile_expression('foo == 42')
+ >>> expr(foo=23)
+ False
+ >>> expr(foo=42)
+ True
+
+ Per default the return value is converted to `None` if the
+ expression returns an undefined value. This can be changed
+ by setting `undefined_to_none` to `False`.
+
+ >>> env.compile_expression('var')() is None
+ True
+ >>> env.compile_expression('var', undefined_to_none=False)()
+ Undefined
+
+ .. versionadded:: 2.1
+ """
+ parser = Parser(self, source, state='variable')
+ exc_info = None
+ try:
+ expr = parser.parse_expression()
+ if not parser.stream.eos:
+ raise TemplateSyntaxError('chunk after expression',
+ parser.stream.current.lineno,
+ None, None)
+ expr.set_environment(self)
+ except TemplateSyntaxError:
+ exc_info = sys.exc_info()
+ if exc_info is not None:
+ self.handle_exception(exc_info, source_hint=source)
+ body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)]
+ template = self.from_string(nodes.Template(body, lineno=1))
+ return TemplateExpression(template, undefined_to_none)
+
+ def compile_templates(self, target, extensions=None, filter_func=None,
+ zip='deflated', log_function=None,
+ ignore_errors=True, py_compile=False):
+ """Finds all the templates the loader can find, compiles them
+ and stores them in `target`. If `zip` is `None`, instead of in a
+ zipfile, the templates will be will be stored in a directory.
+ By default a deflate zip algorithm is used, to switch to
+ the stored algorithm, `zip` can be set to ``'stored'``.
+
+ `extensions` and `filter_func` are passed to :meth:`list_templates`.
+ Each template returned will be compiled to the target folder or
+ zipfile.
+
+ By default template compilation errors are ignored. In case a
+ log function is provided, errors are logged. If you want template
+ syntax errors to abort the compilation you can set `ignore_errors`
+ to `False` and you will get an exception on syntax errors.
+
+ If `py_compile` is set to `True` .pyc files will be written to the
+ target instead of standard .py files.
+
+ .. versionadded:: 2.4
+ """
+ from jinja2.loaders import ModuleLoader
+
+ if log_function is None:
+ log_function = lambda x: None
+
+ if py_compile:
+ import imp, marshal
+ py_header = imp.get_magic() + \
+ u'\xff\xff\xff\xff'.encode('iso-8859-15')
+
+ def write_file(filename, data, mode):
+ if zip:
+ info = ZipInfo(filename)
+ info.external_attr = 0755 << 16L
+ zip_file.writestr(info, data)
+ else:
+ f = open(os.path.join(target, filename), mode)
+ try:
+ f.write(data)
+ finally:
+ f.close()
+
+ if zip is not None:
+ from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
+ zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED,
+ stored=ZIP_STORED)[zip])
+ log_function('Compiling into Zip archive "%s"' % target)
+ else:
+ if not os.path.isdir(target):
+ os.makedirs(target)
+ log_function('Compiling into folder "%s"' % target)
+
+ try:
+ for name in self.list_templates(extensions, filter_func):
+ source, filename, _ = self.loader.get_source(self, name)
+ try:
+ code = self.compile(source, name, filename, True, True)
+ except TemplateSyntaxError, e:
+ if not ignore_errors:
+ raise
+ log_function('Could not compile "%s": %s' % (name, e))
+ continue
+
+ filename = ModuleLoader.get_module_filename(name)
+
+ if py_compile:
+ c = self._compile(code, _encode_filename(filename))
+ write_file(filename + 'c', py_header +
+ marshal.dumps(c), 'wb')
+ log_function('Byte-compiled "%s" as %s' %
+ (name, filename + 'c'))
+ else:
+ write_file(filename, code, 'w')
+ log_function('Compiled "%s" as %s' % (name, filename))
+ finally:
+ if zip:
+ zip_file.close()
+
+ log_function('Finished compiling templates')
+
+ def list_templates(self, extensions=None, filter_func=None):
+ """Returns a list of templates for this environment. This requires
+ that the loader supports the loader's
+ :meth:`~BaseLoader.list_templates` method.
+
+ If there are other files in the template folder besides the
+ actual templates, the returned list can be filtered. There are two
+ ways: either `extensions` is set to a list of file extensions for
+ templates, or a `filter_func` can be provided which is a callable that
+ is passed a template name and should return `True` if it should end up
+ in the result list.
+
+ If the loader does not support that, a :exc:`TypeError` is raised.
+
+ .. versionadded:: 2.4
+ """
+ x = self.loader.list_templates()
+ if extensions is not None:
+ if filter_func is not None:
+ raise TypeError('either extensions or filter_func '
+ 'can be passed, but not both')
+ filter_func = lambda x: '.' in x and \
+ x.rsplit('.', 1)[1] in extensions
+ if filter_func is not None:
+ x = filter(filter_func, x)
+ return x
+
+ def handle_exception(self, exc_info=None, rendered=False, source_hint=None):
+ """Exception handling helper. This is used internally to either raise
+ rewritten exceptions or return a rendered traceback for the template.
+ """
+ global _make_traceback
+ if exc_info is None:
+ exc_info = sys.exc_info()
+
+ # the debugging module is imported when it's used for the first time.
+ # we're doing a lot of stuff there and for applications that do not
+ # get any exceptions in template rendering there is no need to load
+ # all of that.
+ if _make_traceback is None:
+ from jinja2.debug import make_traceback as _make_traceback
+ traceback = _make_traceback(exc_info, source_hint)
+ if rendered and self.exception_formatter is not None:
+ return self.exception_formatter(traceback)
+ if self.exception_handler is not None:
+ self.exception_handler(traceback)
+ exc_type, exc_value, tb = traceback.standard_exc_info
+ raise exc_type, exc_value, tb
+
+ def join_path(self, template, parent):
+ """Join a template with the parent. By default all the lookups are
+ relative to the loader root so this method returns the `template`
+ parameter unchanged, but if the paths should be relative to the
+ parent template, this function can be used to calculate the real
+ template name.
+
+ Subclasses may override this method and implement template path
+ joining here.
+ """
+ return template
+
+ @internalcode
+ def _load_template(self, name, globals):
+ if self.loader is None:
+ raise TypeError('no loader for this environment specified')
+ if self.cache is not None:
+ template = self.cache.get(name)
+ if template is not None and (not self.auto_reload or \
+ template.is_up_to_date):
+ return template
+ template = self.loader.load(self, name, globals)
+ if self.cache is not None:
+ self.cache[name] = template
+ return template
+
+ @internalcode
+ def get_template(self, name, parent=None, globals=None):
+ """Load a template from the loader. If a loader is configured this
+ method ask the loader for the template and returns a :class:`Template`.
+ If the `parent` parameter is not `None`, :meth:`join_path` is called
+ to get the real template name before loading.
+
+ The `globals` parameter can be used to provide template wide globals.
+ These variables are available in the context at render time.
+
+ If the template does not exist a :exc:`TemplateNotFound` exception is
+ raised.
+
+ .. versionchanged:: 2.4
+ If `name` is a :class:`Template` object it is returned from the
+ function unchanged.
+ """
+ if isinstance(name, Template):
+ return name
+ if parent is not None:
+ name = self.join_path(name, parent)
+ return self._load_template(name, self.make_globals(globals))
+
+ @internalcode
+ def select_template(self, names, parent=None, globals=None):
+ """Works like :meth:`get_template` but tries a number of templates
+ before it fails. If it cannot find any of the templates, it will
+ raise a :exc:`TemplatesNotFound` exception.
+
+ .. versionadded:: 2.3
+
+ .. versionchanged:: 2.4
+ If `names` contains a :class:`Template` object it is returned
+ from the function unchanged.
+ """
+ if not names:
+ raise TemplatesNotFound(message=u'Tried to select from an empty list '
+ u'of templates.')
+ globals = self.make_globals(globals)
+ for name in names:
+ if isinstance(name, Template):
+ return name
+ if parent is not None:
+ name = self.join_path(name, parent)
+ try:
+ return self._load_template(name, globals)
+ except TemplateNotFound:
+ pass
+ raise TemplatesNotFound(names)
+
+ @internalcode
+ def get_or_select_template(self, template_name_or_list,
+ parent=None, globals=None):
+ """Does a typecheck and dispatches to :meth:`select_template`
+ if an iterable of template names is given, otherwise to
+ :meth:`get_template`.
+
+ .. versionadded:: 2.3
+ """
+ if isinstance(template_name_or_list, basestring):
+ return self.get_template(template_name_or_list, parent, globals)
+ elif isinstance(template_name_or_list, Template):
+ return template_name_or_list
+ return self.select_template(template_name_or_list, parent, globals)
+
+ def from_string(self, source, globals=None, template_class=None):
+ """Load a template from a string. This parses the source given and
+ returns a :class:`Template` object.
+ """
+ globals = self.make_globals(globals)
+ cls = template_class or self.template_class
+ return cls.from_code(self, self.compile(source), globals, None)
+
+ def make_globals(self, d):
+ """Return a dict for the globals."""
+ if not d:
+ return self.globals
+ return dict(self.globals, **d)
+
+
+class Template(object):
+ """The central template object. This class represents a compiled template
+ and is used to evaluate it.
+
+ Normally the template object is generated from an :class:`Environment` but
+ it also has a constructor that makes it possible to create a template
+ instance directly using the constructor. It takes the same arguments as
+ the environment constructor but it's not possible to specify a loader.
+
+ Every template object has a few methods and members that are guaranteed
+ to exist. However it's important that a template object should be
+ considered immutable. Modifications on the object are not supported.
+
+ Template objects created from the constructor rather than an environment
+ do have an `environment` attribute that points to a temporary environment
+ that is probably shared with other templates created with the constructor
+ and compatible settings.
+
+ >>> template = Template('Hello {{ name }}!')
+ >>> template.render(name='John Doe')
+ u'Hello John Doe!'
+
+ >>> stream = template.stream(name='John Doe')
+ >>> stream.next()
+ u'Hello John Doe!'
+ >>> stream.next()
+ Traceback (most recent call last):
+ ...
+ StopIteration
+ """
+
+ def __new__(cls, source,
+ block_start_string=BLOCK_START_STRING,
+ block_end_string=BLOCK_END_STRING,
+ variable_start_string=VARIABLE_START_STRING,
+ variable_end_string=VARIABLE_END_STRING,
+ comment_start_string=COMMENT_START_STRING,
+ comment_end_string=COMMENT_END_STRING,
+ line_statement_prefix=LINE_STATEMENT_PREFIX,
+ line_comment_prefix=LINE_COMMENT_PREFIX,
+ trim_blocks=TRIM_BLOCKS,
+ newline_sequence=NEWLINE_SEQUENCE,
+ extensions=(),
+ optimized=True,
+ undefined=Undefined,
+ finalize=None,
+ autoescape=False):
+ env = get_spontaneous_environment(
+ block_start_string, block_end_string, variable_start_string,
+ variable_end_string, comment_start_string, comment_end_string,
+ line_statement_prefix, line_comment_prefix, trim_blocks,
+ newline_sequence, frozenset(extensions), optimized, undefined,
+ finalize, autoescape, None, 0, False, None)
+ return env.from_string(source, template_class=cls)
+
+ @classmethod
+ def from_code(cls, environment, code, globals, uptodate=None):
+ """Creates a template object from compiled code and the globals. This
+ is used by the loaders and environment to create a template object.
+ """
+ namespace = {
+ 'environment': environment,
+ '__file__': code.co_filename
+ }
+ exec code in namespace
+ rv = cls._from_namespace(environment, namespace, globals)
+ rv._uptodate = uptodate
+ return rv
+
+ @classmethod
+ def from_module_dict(cls, environment, module_dict, globals):
+ """Creates a template object from a module. This is used by the
+ module loader to create a template object.
+
+ .. versionadded:: 2.4
+ """
+ return cls._from_namespace(environment, module_dict, globals)
+
+ @classmethod
+ def _from_namespace(cls, environment, namespace, globals):
+ t = object.__new__(cls)
+ t.environment = environment
+ t.globals = globals
+ t.name = namespace['name']
+ t.filename = namespace['__file__']
+ t.blocks = namespace['blocks']
+
+ # render function and module
+ t.root_render_func = namespace['root']
+ t._module = None
+
+ # debug and loader helpers
+ t._debug_info = namespace['debug_info']
+ t._uptodate = None
+
+ # store the reference
+ namespace['environment'] = environment
+ namespace['__jinja_template__'] = t
+
+ return t
+
+ def render(self, *args, **kwargs):
+ """This method accepts the same arguments as the `dict` constructor:
+ A dict, a dict subclass or some keyword arguments. If no arguments
+ are given the context will be empty. These two calls do the same::
+
+ template.render(knights='that say nih')
+ template.render({'knights': 'that say nih'})
+
+ This will return the rendered template as unicode string.
+ """
+ vars = dict(*args, **kwargs)
+ try:
+ return concat(self.root_render_func(self.new_context(vars)))
+ except Exception:
+ exc_info = sys.exc_info()
+ return self.environment.handle_exception(exc_info, True)
+
+ def stream(self, *args, **kwargs):
+ """Works exactly like :meth:`generate` but returns a
+ :class:`TemplateStream`.
+ """
+ return TemplateStream(self.generate(*args, **kwargs))
+
+ def generate(self, *args, **kwargs):
+ """For very large templates it can be useful to not render the whole
+ template at once but evaluate each statement after another and yield
+ piece for piece. This method basically does exactly that and returns
+ a generator that yields one item after another as unicode strings.
+
+ It accepts the same arguments as :meth:`render`.
+ """
+ vars = dict(*args, **kwargs)
+ try:
+ for event in self.root_render_func(self.new_context(vars)):
+ yield event
+ except Exception:
+ exc_info = sys.exc_info()
+ else:
+ return
+ yield self.environment.handle_exception(exc_info, True)
+
+ def new_context(self, vars=None, shared=False, locals=None):
+ """Create a new :class:`Context` for this template. The vars
+ provided will be passed to the template. Per default the globals
+ are added to the context. If shared is set to `True` the data
+ is passed as it to the context without adding the globals.
+
+ `locals` can be a dict of local variables for internal usage.
+ """
+ return new_context(self.environment, self.name, self.blocks,
+ vars, shared, self.globals, locals)
+
+ def make_module(self, vars=None, shared=False, locals=None):
+ """This method works like the :attr:`module` attribute when called
+ without arguments but it will evaluate the template on every call
+ rather than caching it. It's also possible to provide
+ a dict which is then used as context. The arguments are the same
+ as for the :meth:`new_context` method.
+ """
+ return TemplateModule(self, self.new_context(vars, shared, locals))
+
+ @property
+ def module(self):
+ """The template as module. This is used for imports in the
+ template runtime but is also useful if one wants to access
+ exported template variables from the Python layer:
+
+ >>> t = Template('{% macro foo() %}42{% endmacro %}23')
+ >>> unicode(t.module)
+ u'23'
+ >>> t.module.foo()
+ u'42'
+ """
+ if self._module is not None:
+ return self._module
+ self._module = rv = self.make_module()
+ return rv
+
+ def get_corresponding_lineno(self, lineno):
+ """Return the source line number of a line number in the
+ generated bytecode as they are not in sync.
+ """
+ for template_line, code_line in reversed(self.debug_info):
+ if code_line <= lineno:
+ return template_line
+ return 1
+
+ @property
+ def is_up_to_date(self):
+ """If this variable is `False` there is a newer version available."""
+ if self._uptodate is None:
+ return True
+ return self._uptodate()
+
+ @property
+ def debug_info(self):
+ """The debug info mapping."""
+ return [tuple(map(int, x.split('='))) for x in
+ self._debug_info.split('&')]
+
+ def __repr__(self):
+ if self.name is None:
+ name = 'memory:%x' % id(self)
+ else:
+ name = repr(self.name)
+ return '<%s %s>' % (self.__class__.__name__, name)
+
+
+class TemplateModule(object):
+ """Represents an imported template. All the exported names of the
+ template are available as attributes on this object. Additionally
+ converting it into an unicode- or bytestrings renders the contents.
+ """
+
+ def __init__(self, template, context):
+ self._body_stream = list(template.root_render_func(context))
+ self.__dict__.update(context.get_exported())
+ self.__name__ = template.name
+
+ def __html__(self):
+ return Markup(concat(self._body_stream))
+
+ def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ # unicode goes after __str__ because we configured 2to3 to rename
+ # __unicode__ to __str__. because the 2to3 tree is not designed to
+ # remove nodes from it, we leave the above __str__ around and let
+ # it override at runtime.
+ def __unicode__(self):
+ return concat(self._body_stream)
+
+ def __repr__(self):
+ if self.__name__ is None:
+ name = 'memory:%x' % id(self)
+ else:
+ name = repr(self.__name__)
+ return '<%s %s>' % (self.__class__.__name__, name)
+
+
+class TemplateExpression(object):
+ """The :meth:`jinja2.Environment.compile_expression` method returns an
+ instance of this object. It encapsulates the expression-like access
+ to the template with an expression it wraps.
+ """
+
+ def __init__(self, template, undefined_to_none):
+ self._template = template
+ self._undefined_to_none = undefined_to_none
+
+ def __call__(self, *args, **kwargs):
+ context = self._template.new_context(dict(*args, **kwargs))
+ consume(self._template.root_render_func(context))
+ rv = context.vars['result']
+ if self._undefined_to_none and isinstance(rv, Undefined):
+ rv = None
+ return rv
+
+
+class TemplateStream(object):
+ """A template stream works pretty much like an ordinary python generator
+ but it can buffer multiple items to reduce the number of total iterations.
+ Per default the output is unbuffered which means that for every unbuffered
+ instruction in the template one unicode string is yielded.
+
+ If buffering is enabled with a buffer size of 5, five items are combined
+ into a new unicode string. This is mainly useful if you are streaming
+ big templates to a client via WSGI which flushes after each iteration.
+ """
+
+ def __init__(self, gen):
+ self._gen = gen
+ self.disable_buffering()
+
+ def dump(self, fp, encoding=None, errors='strict'):
+ """Dump the complete stream into a file or file-like object.
+ Per default unicode strings are written, if you want to encode
+ before writing specify an `encoding`.
+
+ Example usage::
+
+ Template('Hello {{ name }}!').stream(name='foo').dump('hello.html')
+ """
+ close = False
+ if isinstance(fp, basestring):
+ fp = file(fp, 'w')
+ close = True
+ try:
+ if encoding is not None:
+ iterable = (x.encode(encoding, errors) for x in self)
+ else:
+ iterable = self
+ if hasattr(fp, 'writelines'):
+ fp.writelines(iterable)
+ else:
+ for item in iterable:
+ fp.write(item)
+ finally:
+ if close:
+ fp.close()
+
+ def disable_buffering(self):
+ """Disable the output buffering."""
+ self._next = self._gen.next
+ self.buffered = False
+
+ def enable_buffering(self, size=5):
+ """Enable buffering. Buffer `size` items before yielding them."""
+ if size <= 1:
+ raise ValueError('buffer size too small')
+
+ def generator(next):
+ buf = []
+ c_size = 0
+ push = buf.append
+
+ while 1:
+ try:
+ while c_size < size:
+ c = next()
+ push(c)
+ if c:
+ c_size += 1
+ except StopIteration:
+ if not c_size:
+ return
+ yield concat(buf)
+ del buf[:]
+ c_size = 0
+
+ self.buffered = True
+ self._next = generator(self._gen.next).next
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ return self._next()
+
+
+# hook in default template class. if anyone reads this comment: ignore that
+# it's possible to use custom templates ;-)
+Environment.template_class = Template
diff --git a/websdk/jinja2/exceptions.py b/websdk/jinja2/exceptions.py
new file mode 100644
index 0000000..771f6a8
--- /dev/null
+++ b/websdk/jinja2/exceptions.py
@@ -0,0 +1,143 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.exceptions
+ ~~~~~~~~~~~~~~~~~
+
+ Jinja exceptions.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+
+
+class TemplateError(Exception):
+ """Baseclass for all template errors."""
+
+ def __init__(self, message=None):
+ if message is not None:
+ message = unicode(message).encode('utf-8')
+ Exception.__init__(self, message)
+
+ @property
+ def message(self):
+ if self.args:
+ message = self.args[0]
+ if message is not None:
+ return message.decode('utf-8', 'replace')
+
+
+class TemplateNotFound(IOError, LookupError, TemplateError):
+ """Raised if a template does not exist."""
+
+ # looks weird, but removes the warning descriptor that just
+ # bogusly warns us about message being deprecated
+ message = None
+
+ def __init__(self, name, message=None):
+ IOError.__init__(self)
+ if message is None:
+ message = name
+ self.message = message
+ self.name = name
+ self.templates = [name]
+
+ def __str__(self):
+ return self.message.encode('utf-8')
+
+ # unicode goes after __str__ because we configured 2to3 to rename
+ # __unicode__ to __str__. because the 2to3 tree is not designed to
+ # remove nodes from it, we leave the above __str__ around and let
+ # it override at runtime.
+ def __unicode__(self):
+ return self.message
+
+
+class TemplatesNotFound(TemplateNotFound):
+ """Like :class:`TemplateNotFound` but raised if multiple templates
+ are selected. This is a subclass of :class:`TemplateNotFound`
+ exception, so just catching the base exception will catch both.
+
+ .. versionadded:: 2.2
+ """
+
+ def __init__(self, names=(), message=None):
+ if message is None:
+ message = u'non of the templates given were found: ' + \
+ u', '.join(map(unicode, names))
+ TemplateNotFound.__init__(self, names and names[-1] or None, message)
+ self.templates = list(names)
+
+
+class TemplateSyntaxError(TemplateError):
+ """Raised to tell the user that there is a problem with the template."""
+
+ def __init__(self, message, lineno, name=None, filename=None):
+ TemplateError.__init__(self, message)
+ self.lineno = lineno
+ self.name = name
+ self.filename = filename
+ self.source = None
+
+ # this is set to True if the debug.translate_syntax_error
+ # function translated the syntax error into a new traceback
+ self.translated = False
+
+ def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ # unicode goes after __str__ because we configured 2to3 to rename
+ # __unicode__ to __str__. because the 2to3 tree is not designed to
+ # remove nodes from it, we leave the above __str__ around and let
+ # it override at runtime.
+ def __unicode__(self):
+ # for translated errors we only return the message
+ if self.translated:
+ return self.message
+
+ # otherwise attach some stuff
+ location = 'line %d' % self.lineno
+ name = self.filename or self.name
+ if name:
+ location = 'File "%s", %s' % (name, location)
+ lines = [self.message, ' ' + location]
+
+ # if the source is set, add the line to the output
+ if self.source is not None:
+ try:
+ line = self.source.splitlines()[self.lineno - 1]
+ except IndexError:
+ line = None
+ if line:
+ lines.append(' ' + line.strip())
+
+ return u'\n'.join(lines)
+
+
+class TemplateAssertionError(TemplateSyntaxError):
+ """Like a template syntax error, but covers cases where something in the
+ template caused an error at compile time that wasn't necessarily caused
+ by a syntax error. However it's a direct subclass of
+ :exc:`TemplateSyntaxError` and has the same attributes.
+ """
+
+
+class TemplateRuntimeError(TemplateError):
+ """A generic runtime error in the template engine. Under some situations
+ Jinja may raise this exception.
+ """
+
+
+class UndefinedError(TemplateRuntimeError):
+ """Raised if a template tries to operate on :class:`Undefined`."""
+
+
+class SecurityError(TemplateRuntimeError):
+ """Raised if a template tries to do something insecure if the
+ sandbox is enabled.
+ """
+
+
+class FilterArgumentError(TemplateRuntimeError):
+ """This error is raised if a filter was called with inappropriate
+ arguments
+ """
diff --git a/websdk/jinja2/ext.py b/websdk/jinja2/ext.py
new file mode 100644
index 0000000..5ba6efd
--- /dev/null
+++ b/websdk/jinja2/ext.py
@@ -0,0 +1,612 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.ext
+ ~~~~~~~~~~
+
+ Jinja extensions allow to add custom tags similar to the way django custom
+ tags work. By default two example extensions exist: an i18n and a cache
+ extension.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD.
+"""
+from collections import deque
+from jinja2 import nodes
+from jinja2.defaults import *
+from jinja2.environment import Environment
+from jinja2.runtime import Undefined, concat
+from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
+from jinja2.utils import contextfunction, import_string, Markup, next
+
+
+# the only real useful gettext functions for a Jinja template. Note
+# that ugettext must be assigned to gettext as Jinja doesn't support
+# non unicode strings.
+GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext')
+
+
+class ExtensionRegistry(type):
+ """Gives the extension an unique identifier."""
+
+ def __new__(cls, name, bases, d):
+ rv = type.__new__(cls, name, bases, d)
+ rv.identifier = rv.__module__ + '.' + rv.__name__
+ return rv
+
+
+class Extension(object):
+ """Extensions can be used to add extra functionality to the Jinja template
+ system at the parser level. Custom extensions are bound to an environment
+ but may not store environment specific data on `self`. The reason for
+ this is that an extension can be bound to another environment (for
+ overlays) by creating a copy and reassigning the `environment` attribute.
+
+ As extensions are created by the environment they cannot accept any
+ arguments for configuration. One may want to work around that by using
+ a factory function, but that is not possible as extensions are identified
+ by their import name. The correct way to configure the extension is
+ storing the configuration values on the environment. Because this way the
+ environment ends up acting as central configuration storage the
+ attributes may clash which is why extensions have to ensure that the names
+ they choose for configuration are not too generic. ``prefix`` for example
+ is a terrible name, ``fragment_cache_prefix`` on the other hand is a good
+ name as includes the name of the extension (fragment cache).
+ """
+ __metaclass__ = ExtensionRegistry
+
+ #: if this extension parses this is the list of tags it's listening to.
+ tags = set()
+
+ #: the priority of that extension. This is especially useful for
+ #: extensions that preprocess values. A lower value means higher
+ #: priority.
+ #:
+ #: .. versionadded:: 2.4
+ priority = 100
+
+ def __init__(self, environment):
+ self.environment = environment
+
+ def bind(self, environment):
+ """Create a copy of this extension bound to another environment."""
+ rv = object.__new__(self.__class__)
+ rv.__dict__.update(self.__dict__)
+ rv.environment = environment
+ return rv
+
+ def preprocess(self, source, name, filename=None):
+ """This method is called before the actual lexing and can be used to
+ preprocess the source. The `filename` is optional. The return value
+ must be the preprocessed source.
+ """
+ return source
+
+ def filter_stream(self, stream):
+ """It's passed a :class:`~jinja2.lexer.TokenStream` that can be used
+ to filter tokens returned. This method has to return an iterable of
+ :class:`~jinja2.lexer.Token`\s, but it doesn't have to return a
+ :class:`~jinja2.lexer.TokenStream`.
+
+ In the `ext` folder of the Jinja2 source distribution there is a file
+ called `inlinegettext.py` which implements a filter that utilizes this
+ method.
+ """
+ return stream
+
+ def parse(self, parser):
+ """If any of the :attr:`tags` matched this method is called with the
+ parser as first argument. The token the parser stream is pointing at
+ is the name token that matched. This method has to return one or a
+ list of multiple nodes.
+ """
+ raise NotImplementedError()
+
+ def attr(self, name, lineno=None):
+ """Return an attribute node for the current extension. This is useful
+ to pass constants on extensions to generated template code.
+
+ ::
+
+ self.attr('_my_attribute', lineno=lineno)
+ """
+ return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
+
+ def call_method(self, name, args=None, kwargs=None, dyn_args=None,
+ dyn_kwargs=None, lineno=None):
+ """Call a method of the extension. This is a shortcut for
+ :meth:`attr` + :class:`jinja2.nodes.Call`.
+ """
+ if args is None:
+ args = []
+ if kwargs is None:
+ kwargs = []
+ return nodes.Call(self.attr(name, lineno=lineno), args, kwargs,
+ dyn_args, dyn_kwargs, lineno=lineno)
+
+
+@contextfunction
+def _gettext_alias(__context, *args, **kwargs):
+ return __context.call(__context.resolve('gettext'), *args, **kwargs)
+
+
+def _make_new_gettext(func):
+ @contextfunction
+ def gettext(__context, __string, **variables):
+ rv = __context.call(func, __string)
+ if __context.eval_ctx.autoescape:
+ rv = Markup(rv)
+ return rv % variables
+ return gettext
+
+
+def _make_new_ngettext(func):
+ @contextfunction
+ def ngettext(__context, __singular, __plural, __num, **variables):
+ variables.setdefault('num', __num)
+ rv = __context.call(func, __singular, __plural, __num)
+ if __context.eval_ctx.autoescape:
+ rv = Markup(rv)
+ return rv % variables
+ return ngettext
+
+
+class InternationalizationExtension(Extension):
+ """This extension adds gettext support to Jinja2."""
+ tags = set(['trans'])
+
+ # TODO: the i18n extension is currently reevaluating values in a few
+ # situations. Take this example:
+ # {% trans count=something() %}{{ count }} foo{% pluralize
+ # %}{{ count }} fooss{% endtrans %}
+ # something is called twice here. One time for the gettext value and
+ # the other time for the n-parameter of the ngettext function.
+
+ def __init__(self, environment):
+ Extension.__init__(self, environment)
+ environment.globals['_'] = _gettext_alias
+ environment.extend(
+ install_gettext_translations=self._install,
+ install_null_translations=self._install_null,
+ install_gettext_callables=self._install_callables,
+ uninstall_gettext_translations=self._uninstall,
+ extract_translations=self._extract,
+ newstyle_gettext=False
+ )
+
+ def _install(self, translations, newstyle=None):
+ gettext = getattr(translations, 'ugettext', None)
+ if gettext is None:
+ gettext = translations.gettext
+ ngettext = getattr(translations, 'ungettext', None)
+ if ngettext is None:
+ ngettext = translations.ngettext
+ self._install_callables(gettext, ngettext, newstyle)
+
+ def _install_null(self, newstyle=None):
+ self._install_callables(
+ lambda x: x,
+ lambda s, p, n: (n != 1 and (p,) or (s,))[0],
+ newstyle
+ )
+
+ def _install_callables(self, gettext, ngettext, newstyle=None):
+ if newstyle is not None:
+ self.environment.newstyle_gettext = newstyle
+ if self.environment.newstyle_gettext:
+ gettext = _make_new_gettext(gettext)
+ ngettext = _make_new_ngettext(ngettext)
+ self.environment.globals.update(
+ gettext=gettext,
+ ngettext=ngettext
+ )
+
+ def _uninstall(self, translations):
+ for key in 'gettext', 'ngettext':
+ self.environment.globals.pop(key, None)
+
+ def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
+ if isinstance(source, basestring):
+ source = self.environment.parse(source)
+ return extract_from_ast(source, gettext_functions)
+
+ def parse(self, parser):
+ """Parse a translatable tag."""
+ lineno = next(parser.stream).lineno
+ num_called_num = False
+
+ # find all the variables referenced. Additionally a variable can be
+ # defined in the body of the trans block too, but this is checked at
+ # a later state.
+ plural_expr = None
+ variables = {}
+ while parser.stream.current.type != 'block_end':
+ if variables:
+ parser.stream.expect('comma')
+
+ # skip colon for python compatibility
+ if parser.stream.skip_if('colon'):
+ break
+
+ name = parser.stream.expect('name')
+ if name.value in variables:
+ parser.fail('translatable variable %r defined twice.' %
+ name.value, name.lineno,
+ exc=TemplateAssertionError)
+
+ # expressions
+ if parser.stream.current.type == 'assign':
+ next(parser.stream)
+ variables[name.value] = var = parser.parse_expression()
+ else:
+ variables[name.value] = var = nodes.Name(name.value, 'load')
+
+ if plural_expr is None:
+ plural_expr = var
+ num_called_num = name.value == 'num'
+
+ parser.stream.expect('block_end')
+
+ plural = plural_names = None
+ have_plural = False
+ referenced = set()
+
+ # now parse until endtrans or pluralize
+ singular_names, singular = self._parse_block(parser, True)
+ if singular_names:
+ referenced.update(singular_names)
+ if plural_expr is None:
+ plural_expr = nodes.Name(singular_names[0], 'load')
+ num_called_num = singular_names[0] == 'num'
+
+ # if we have a pluralize block, we parse that too
+ if parser.stream.current.test('name:pluralize'):
+ have_plural = True
+ next(parser.stream)
+ if parser.stream.current.type != 'block_end':
+ name = parser.stream.expect('name')
+ if name.value not in variables:
+ parser.fail('unknown variable %r for pluralization' %
+ name.value, name.lineno,
+ exc=TemplateAssertionError)
+ plural_expr = variables[name.value]
+ num_called_num = name.value == 'num'
+ parser.stream.expect('block_end')
+ plural_names, plural = self._parse_block(parser, False)
+ next(parser.stream)
+ referenced.update(plural_names)
+ else:
+ next(parser.stream)
+
+ # register free names as simple name expressions
+ for var in referenced:
+ if var not in variables:
+ variables[var] = nodes.Name(var, 'load')
+
+ if not have_plural:
+ plural_expr = None
+ elif plural_expr is None:
+ parser.fail('pluralize without variables', lineno)
+
+ node = self._make_node(singular, plural, variables, plural_expr,
+ bool(referenced),
+ num_called_num and have_plural)
+ node.set_lineno(lineno)
+ return node
+
+ def _parse_block(self, parser, allow_pluralize):
+ """Parse until the next block tag with a given name."""
+ referenced = []
+ buf = []
+ while 1:
+ if parser.stream.current.type == 'data':
+ buf.append(parser.stream.current.value.replace('%', '%%'))
+ next(parser.stream)
+ elif parser.stream.current.type == 'variable_begin':
+ next(parser.stream)
+ name = parser.stream.expect('name').value
+ referenced.append(name)
+ buf.append('%%(%s)s' % name)
+ parser.stream.expect('variable_end')
+ elif parser.stream.current.type == 'block_begin':
+ next(parser.stream)
+ if parser.stream.current.test('name:endtrans'):
+ break
+ elif parser.stream.current.test('name:pluralize'):
+ if allow_pluralize:
+ break
+ parser.fail('a translatable section can have only one '
+ 'pluralize section')
+ parser.fail('control structures in translatable sections are '
+ 'not allowed')
+ elif parser.stream.eos:
+ parser.fail('unclosed translation block')
+ else:
+ assert False, 'internal parser error'
+
+ return referenced, concat(buf)
+
+ def _make_node(self, singular, plural, variables, plural_expr,
+ vars_referenced, num_called_num):
+ """Generates a useful node from the data provided."""
+ # no variables referenced? no need to escape for old style
+ # gettext invocations only if there are vars.
+ if not vars_referenced and not self.environment.newstyle_gettext:
+ singular = singular.replace('%%', '%')
+ if plural:
+ plural = plural.replace('%%', '%')
+
+ # singular only:
+ if plural_expr is None:
+ gettext = nodes.Name('gettext', 'load')
+ node = nodes.Call(gettext, [nodes.Const(singular)],
+ [], None, None)
+
+ # singular and plural
+ else:
+ ngettext = nodes.Name('ngettext', 'load')
+ node = nodes.Call(ngettext, [
+ nodes.Const(singular),
+ nodes.Const(plural),
+ plural_expr
+ ], [], None, None)
+
+ # in case newstyle gettext is used, the method is powerful
+ # enough to handle the variable expansion and autoescape
+ # handling itself
+ if self.environment.newstyle_gettext:
+ for key, value in variables.iteritems():
+ # the function adds that later anyways in case num was
+ # called num, so just skip it.
+ if num_called_num and key == 'num':
+ continue
+ node.kwargs.append(nodes.Keyword(key, value))
+
+ # otherwise do that here
+ else:
+ # mark the return value as safe if we are in an
+ # environment with autoescaping turned on
+ node = nodes.MarkSafeIfAutoescape(node)
+ if variables:
+ node = nodes.Mod(node, nodes.Dict([
+ nodes.Pair(nodes.Const(key), value)
+ for key, value in variables.items()
+ ]))
+ return nodes.Output([node])
+
+
+class ExprStmtExtension(Extension):
+ """Adds a `do` tag to Jinja2 that works like the print statement just
+ that it doesn't print the return value.
+ """
+ tags = set(['do'])
+
+ def parse(self, parser):
+ node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
+ node.node = parser.parse_tuple()
+ return node
+
+
+class LoopControlExtension(Extension):
+ """Adds break and continue to the template engine."""
+ tags = set(['break', 'continue'])
+
+ def parse(self, parser):
+ token = next(parser.stream)
+ if token.value == 'break':
+ return nodes.Break(lineno=token.lineno)
+ return nodes.Continue(lineno=token.lineno)
+
+
+class WithExtension(Extension):
+ """Adds support for a django-like with block."""
+ tags = set(['with'])
+
+ def parse(self, parser):
+ node = nodes.Scope(lineno=next(parser.stream).lineno)
+ assignments = []
+ while parser.stream.current.type != 'block_end':
+ lineno = parser.stream.current.lineno
+ if assignments:
+ parser.stream.expect('comma')
+ target = parser.parse_assign_target()
+ parser.stream.expect('assign')
+ expr = parser.parse_expression()
+ assignments.append(nodes.Assign(target, expr, lineno=lineno))
+ node.body = assignments + \
+ list(parser.parse_statements(('name:endwith',),
+ drop_needle=True))
+ return node
+
+
+class AutoEscapeExtension(Extension):
+ """Changes auto escape rules for a scope."""
+ tags = set(['autoescape'])
+
+ def parse(self, parser):
+ node = nodes.ScopedEvalContextModifier(lineno=next(parser.stream).lineno)
+ node.options = [
+ nodes.Keyword('autoescape', parser.parse_expression())
+ ]
+ node.body = parser.parse_statements(('name:endautoescape',),
+ drop_needle=True)
+ return nodes.Scope([node])
+
+
+def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
+ babel_style=True):
+ """Extract localizable strings from the given template node. Per
+ default this function returns matches in babel style that means non string
+ parameters as well as keyword arguments are returned as `None`. This
+ allows Babel to figure out what you really meant if you are using
+ gettext functions that allow keyword arguments for placeholder expansion.
+ If you don't want that behavior set the `babel_style` parameter to `False`
+ which causes only strings to be returned and parameters are always stored
+ in tuples. As a consequence invalid gettext calls (calls without a single
+ string parameter or string parameters after non-string parameters) are
+ skipped.
+
+ This example explains the behavior:
+
+ >>> from jinja2 import Environment
+ >>> env = Environment()
+ >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}')
+ >>> list(extract_from_ast(node))
+ [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))]
+ >>> list(extract_from_ast(node, babel_style=False))
+ [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))]
+
+ For every string found this function yields a ``(lineno, function,
+ message)`` tuple, where:
+
+ * ``lineno`` is the number of the line on which the string was found,
+ * ``function`` is the name of the ``gettext`` function used (if the
+ string was extracted from embedded Python code), and
+ * ``message`` is the string itself (a ``unicode`` object, or a tuple
+ of ``unicode`` objects for functions with multiple string arguments).
+
+ This extraction function operates on the AST and is because of that unable
+ to extract any comments. For comment support you have to use the babel
+ extraction interface or extract comments yourself.
+ """
+ for node in node.find_all(nodes.Call):
+ if not isinstance(node.node, nodes.Name) or \
+ node.node.name not in gettext_functions:
+ continue
+
+ strings = []
+ for arg in node.args:
+ if isinstance(arg, nodes.Const) and \
+ isinstance(arg.value, basestring):
+ strings.append(arg.value)
+ else:
+ strings.append(None)
+
+ for arg in node.kwargs:
+ strings.append(None)
+ if node.dyn_args is not None:
+ strings.append(None)
+ if node.dyn_kwargs is not None:
+ strings.append(None)
+
+ if not babel_style:
+ strings = tuple(x for x in strings if x is not None)
+ if not strings:
+ continue
+ else:
+ if len(strings) == 1:
+ strings = strings[0]
+ else:
+ strings = tuple(strings)
+ yield node.lineno, node.node.name, strings
+
+
+class _CommentFinder(object):
+ """Helper class to find comments in a token stream. Can only
+ find comments for gettext calls forwards. Once the comment
+ from line 4 is found, a comment for line 1 will not return a
+ usable value.
+ """
+
+ def __init__(self, tokens, comment_tags):
+ self.tokens = tokens
+ self.comment_tags = comment_tags
+ self.offset = 0
+ self.last_lineno = 0
+
+ def find_backwards(self, offset):
+ try:
+ for _, token_type, token_value in \
+ reversed(self.tokens[self.offset:offset]):
+ if token_type in ('comment', 'linecomment'):
+ try:
+ prefix, comment = token_value.split(None, 1)
+ except ValueError:
+ continue
+ if prefix in self.comment_tags:
+ return [comment.rstrip()]
+ return []
+ finally:
+ self.offset = offset
+
+ def find_comments(self, lineno):
+ if not self.comment_tags or self.last_lineno > lineno:
+ return []
+ for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]):
+ if token_lineno > lineno:
+ return self.find_backwards(self.offset + idx)
+ return self.find_backwards(len(self.tokens))
+
+
+def babel_extract(fileobj, keywords, comment_tags, options):
+ """Babel extraction method for Jinja templates.
+
+ .. versionchanged:: 2.3
+ Basic support for translation comments was added. If `comment_tags`
+ is now set to a list of keywords for extraction, the extractor will
+ try to find the best preceeding comment that begins with one of the
+ keywords. For best results, make sure to not have more than one
+ gettext call in one line of code and the matching comment in the
+ same line or the line before.
+
+ .. versionchanged:: 2.5.1
+ The `newstyle_gettext` flag can be set to `True` to enable newstyle
+ gettext calls.
+
+ :param fileobj: the file-like object the messages should be extracted from
+ :param keywords: a list of keywords (i.e. function names) that should be
+ recognized as translation functions
+ :param comment_tags: a list of translator tags to search for and include
+ in the results.
+ :param options: a dictionary of additional options (optional)
+ :return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
+ (comments will be empty currently)
+ """
+ extensions = set()
+ for extension in options.get('extensions', '').split(','):
+ extension = extension.strip()
+ if not extension:
+ continue
+ extensions.add(import_string(extension))
+ if InternationalizationExtension not in extensions:
+ extensions.add(InternationalizationExtension)
+
+ def getbool(options, key, default=False):
+ options.get(key, str(default)).lower() in ('1', 'on', 'yes', 'true')
+
+ environment = Environment(
+ options.get('block_start_string', BLOCK_START_STRING),
+ options.get('block_end_string', BLOCK_END_STRING),
+ options.get('variable_start_string', VARIABLE_START_STRING),
+ options.get('variable_end_string', VARIABLE_END_STRING),
+ options.get('comment_start_string', COMMENT_START_STRING),
+ options.get('comment_end_string', COMMENT_END_STRING),
+ options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX,
+ options.get('line_comment_prefix') or LINE_COMMENT_PREFIX,
+ getbool(options, 'trim_blocks', TRIM_BLOCKS),
+ NEWLINE_SEQUENCE, frozenset(extensions),
+ cache_size=0,
+ auto_reload=False
+ )
+
+ if getbool(options, 'newstyle_gettext'):
+ environment.newstyle_gettext = True
+
+ source = fileobj.read().decode(options.get('encoding', 'utf-8'))
+ try:
+ node = environment.parse(source)
+ tokens = list(environment.lex(environment.preprocess(source)))
+ except TemplateSyntaxError, e:
+ # skip templates with syntax errors
+ return
+
+ finder = _CommentFinder(tokens, comment_tags)
+ for lineno, func, message in extract_from_ast(node, keywords):
+ yield lineno, func, message, finder.find_comments(lineno)
+
+
+#: nicer import names
+i18n = InternationalizationExtension
+do = ExprStmtExtension
+loopcontrols = LoopControlExtension
+with_ = WithExtension
+autoescape = AutoEscapeExtension
diff --git a/websdk/jinja2/filters.py b/websdk/jinja2/filters.py
new file mode 100644
index 0000000..1ef47f9
--- /dev/null
+++ b/websdk/jinja2/filters.py
@@ -0,0 +1,801 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.filters
+ ~~~~~~~~~~~~~~
+
+ Bundled jinja filters.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+import math
+from random import choice
+from operator import itemgetter
+from itertools import imap, groupby
+from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode
+from jinja2.runtime import Undefined
+from jinja2.exceptions import FilterArgumentError, SecurityError
+
+
+_word_re = re.compile(r'\w+(?u)')
+
+
+def contextfilter(f):
+ """Decorator for marking context dependent filters. The current
+ :class:`Context` will be passed as first argument.
+ """
+ f.contextfilter = True
+ return f
+
+
+def evalcontextfilter(f):
+ """Decorator for marking eval-context dependent filters. An eval
+ context object is passed as first argument. For more information
+ about the eval context, see :ref:`eval-context`.
+
+ .. versionadded:: 2.4
+ """
+ f.evalcontextfilter = True
+ return f
+
+
+def environmentfilter(f):
+ """Decorator for marking evironment dependent filters. The current
+ :class:`Environment` is passed to the filter as first argument.
+ """
+ f.environmentfilter = True
+ return f
+
+
+def make_attrgetter(environment, attribute):
+ """Returns a callable that looks up the given attribute from a
+ passed object with the rules of the environment. Dots are allowed
+ to access attributes of attributes.
+ """
+ if not isinstance(attribute, basestring) or '.' not in attribute:
+ return lambda x: environment.getitem(x, attribute)
+ attribute = attribute.split('.')
+ def attrgetter(item):
+ for part in attribute:
+ item = environment.getitem(item, part)
+ return item
+ return attrgetter
+
+
+def do_forceescape(value):
+ """Enforce HTML escaping. This will probably double escape variables."""
+ if hasattr(value, '__html__'):
+ value = value.__html__()
+ return escape(unicode(value))
+
+
+@evalcontextfilter
+def do_replace(eval_ctx, s, old, new, count=None):
+ """Return a copy of the value with all occurrences of a substring
+ replaced with a new one. The first argument is the substring
+ that should be replaced, the second is the replacement string.
+ If the optional third argument ``count`` is given, only the first
+ ``count`` occurrences are replaced:
+
+ .. sourcecode:: jinja
+
+ {{ "Hello World"|replace("Hello", "Goodbye") }}
+ -> Goodbye World
+
+ {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
+ -> d'oh, d'oh, aaargh
+ """
+ if count is None:
+ count = -1
+ if not eval_ctx.autoescape:
+ return unicode(s).replace(unicode(old), unicode(new), count)
+ if hasattr(old, '__html__') or hasattr(new, '__html__') and \
+ not hasattr(s, '__html__'):
+ s = escape(s)
+ else:
+ s = soft_unicode(s)
+ return s.replace(soft_unicode(old), soft_unicode(new), count)
+
+
+def do_upper(s):
+ """Convert a value to uppercase."""
+ return soft_unicode(s).upper()
+
+
+def do_lower(s):
+ """Convert a value to lowercase."""
+ return soft_unicode(s).lower()
+
+
+@evalcontextfilter
+def do_xmlattr(_eval_ctx, d, autospace=True):
+ """Create an SGML/XML attribute string based on the items in a dict.
+ All values that are neither `none` nor `undefined` are automatically
+ escaped:
+
+ .. sourcecode:: html+jinja
+
+ <ul{{ {'class': 'my_list', 'missing': none,
+ 'id': 'list-%d'|format(variable)}|xmlattr }}>
+ ...
+ </ul>
+
+ Results in something like this:
+
+ .. sourcecode:: html
+
+ <ul class="my_list" id="list-42">
+ ...
+ </ul>
+
+ As you can see it automatically prepends a space in front of the item
+ if the filter returned something unless the second parameter is false.
+ """
+ rv = u' '.join(
+ u'%s="%s"' % (escape(key), escape(value))
+ for key, value in d.iteritems()
+ if value is not None and not isinstance(value, Undefined)
+ )
+ if autospace and rv:
+ rv = u' ' + rv
+ if _eval_ctx.autoescape:
+ rv = Markup(rv)
+ return rv
+
+
+def do_capitalize(s):
+ """Capitalize a value. The first character will be uppercase, all others
+ lowercase.
+ """
+ return soft_unicode(s).capitalize()
+
+
+def do_title(s):
+ """Return a titlecased version of the value. I.e. words will start with
+ uppercase letters, all remaining characters are lowercase.
+ """
+ return soft_unicode(s).title()
+
+
+def do_dictsort(value, case_sensitive=False, by='key'):
+ """Sort a dict and yield (key, value) pairs. Because python dicts are
+ unsorted you may want to use this function to order them by either
+ key or value:
+
+ .. sourcecode:: jinja
+
+ {% for item in mydict|dictsort %}
+ sort the dict by key, case insensitive
+
+ {% for item in mydict|dicsort(true) %}
+ sort the dict by key, case sensitive
+
+ {% for item in mydict|dictsort(false, 'value') %}
+ sort the dict by key, case insensitive, sorted
+ normally and ordered by value.
+ """
+ if by == 'key':
+ pos = 0
+ elif by == 'value':
+ pos = 1
+ else:
+ raise FilterArgumentError('You can only sort by either '
+ '"key" or "value"')
+ def sort_func(item):
+ value = item[pos]
+ if isinstance(value, basestring) and not case_sensitive:
+ value = value.lower()
+ return value
+
+ return sorted(value.items(), key=sort_func)
+
+
+@environmentfilter
+def do_sort(environment, value, reverse=False, case_sensitive=False,
+ attribute=None):
+ """Sort an iterable. Per default it sorts ascending, if you pass it
+ true as first argument it will reverse the sorting.
+
+ If the iterable is made of strings the third parameter can be used to
+ control the case sensitiveness of the comparison which is disabled by
+ default.
+
+ .. sourcecode:: jinja
+
+ {% for item in iterable|sort %}
+ ...
+ {% endfor %}
+
+ It is also possible to sort by an attribute (for example to sort
+ by the date of an object) by specifying the `attribute` parameter:
+
+ .. sourcecode:: jinja
+
+ {% for item in iterable|sort(attribute='date') %}
+ ...
+ {% endfor %}
+
+ .. versionchanged:: 2.6
+ The `attribute` parameter was added.
+ """
+ if not case_sensitive:
+ def sort_func(item):
+ if isinstance(item, basestring):
+ item = item.lower()
+ return item
+ else:
+ sort_func = None
+ if attribute is not None:
+ getter = make_attrgetter(environment, attribute)
+ def sort_func(item, processor=sort_func or (lambda x: x)):
+ return processor(getter(item))
+ return sorted(value, key=sort_func, reverse=reverse)
+
+
+def do_default(value, default_value=u'', boolean=False):
+ """If the value is undefined it will return the passed default value,
+ otherwise the value of the variable:
+
+ .. sourcecode:: jinja
+
+ {{ my_variable|default('my_variable is not defined') }}
+
+ This will output the value of ``my_variable`` if the variable was
+ defined, otherwise ``'my_variable is not defined'``. If you want
+ to use default with variables that evaluate to false you have to
+ set the second parameter to `true`:
+
+ .. sourcecode:: jinja
+
+ {{ ''|default('the string was empty', true) }}
+ """
+ if (boolean and not value) or isinstance(value, Undefined):
+ return default_value
+ return value
+
+
+@evalcontextfilter
+def do_join(eval_ctx, value, d=u'', attribute=None):
+ """Return a string which is the concatenation of the strings in the
+ sequence. The separator between elements is an empty string per
+ default, you can define it with the optional parameter:
+
+ .. sourcecode:: jinja
+
+ {{ [1, 2, 3]|join('|') }}
+ -> 1|2|3
+
+ {{ [1, 2, 3]|join }}
+ -> 123
+
+ It is also possible to join certain attributes of an object:
+
+ .. sourcecode:: jinja
+
+ {{ users|join(', ', attribute='username') }}
+
+ .. versionadded:: 2.6
+ The `attribute` parameter was added.
+ """
+ if attribute is not None:
+ value = imap(make_attrgetter(eval_ctx.environment, attribute), value)
+
+ # no automatic escaping? joining is a lot eaiser then
+ if not eval_ctx.autoescape:
+ return unicode(d).join(imap(unicode, value))
+
+ # if the delimiter doesn't have an html representation we check
+ # if any of the items has. If yes we do a coercion to Markup
+ if not hasattr(d, '__html__'):
+ value = list(value)
+ do_escape = False
+ for idx, item in enumerate(value):
+ if hasattr(item, '__html__'):
+ do_escape = True
+ else:
+ value[idx] = unicode(item)
+ if do_escape:
+ d = escape(d)
+ else:
+ d = unicode(d)
+ return d.join(value)
+
+ # no html involved, to normal joining
+ return soft_unicode(d).join(imap(soft_unicode, value))
+
+
+def do_center(value, width=80):
+ """Centers the value in a field of a given width."""
+ return unicode(value).center(width)
+
+
+@environmentfilter
+def do_first(environment, seq):
+ """Return the first item of a sequence."""
+ try:
+ return iter(seq).next()
+ except StopIteration:
+ return environment.undefined('No first item, sequence was empty.')
+
+
+@environmentfilter
+def do_last(environment, seq):
+ """Return the last item of a sequence."""
+ try:
+ return iter(reversed(seq)).next()
+ except StopIteration:
+ return environment.undefined('No last item, sequence was empty.')
+
+
+@environmentfilter
+def do_random(environment, seq):
+ """Return a random item from the sequence."""
+ try:
+ return choice(seq)
+ except IndexError:
+ return environment.undefined('No random item, sequence was empty.')
+
+
+def do_filesizeformat(value, binary=False):
+ """Format the value like a 'human-readable' file size (i.e. 13 kB,
+ 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega,
+ Giga, etc.), if the second parameter is set to `True` the binary
+ prefixes are used (Mebi, Gibi).
+ """
+ bytes = float(value)
+ base = binary and 1024 or 1000
+ prefixes = [
+ (binary and "KiB" or "kB"),
+ (binary and "MiB" or "MB"),
+ (binary and "GiB" or "GB"),
+ (binary and "TiB" or "TB"),
+ (binary and "PiB" or "PB"),
+ (binary and "EiB" or "EB"),
+ (binary and "ZiB" or "ZB"),
+ (binary and "YiB" or "YB")
+ ]
+ if bytes == 1:
+ return "1 Byte"
+ elif bytes < base:
+ return "%d Bytes" % bytes
+ else:
+ for i, prefix in enumerate(prefixes):
+ unit = base * base ** (i + 1)
+ if bytes < unit:
+ return "%.1f %s" % ((bytes / unit), prefix)
+ return "%.1f %s" % ((bytes / unit), prefix)
+
+
+def do_pprint(value, verbose=False):
+ """Pretty print a variable. Useful for debugging.
+
+ With Jinja 1.2 onwards you can pass it a parameter. If this parameter
+ is truthy the output will be more verbose (this requires `pretty`)
+ """
+ return pformat(value, verbose=verbose)
+
+
+@evalcontextfilter
+def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False):
+ """Converts URLs in plain text into clickable links.
+
+ If you pass the filter an additional integer it will shorten the urls
+ to that number. Also a third argument exists that makes the urls
+ "nofollow":
+
+ .. sourcecode:: jinja
+
+ {{ mytext|urlize(40, true) }}
+ links are shortened to 40 chars and defined with rel="nofollow"
+ """
+ rv = urlize(value, trim_url_limit, nofollow)
+ if eval_ctx.autoescape:
+ rv = Markup(rv)
+ return rv
+
+
+def do_indent(s, width=4, indentfirst=False):
+ """Return a copy of the passed string, each line indented by
+ 4 spaces. The first line is not indented. If you want to
+ change the number of spaces or indent the first line too
+ you can pass additional parameters to the filter:
+
+ .. sourcecode:: jinja
+
+ {{ mytext|indent(2, true) }}
+ indent by two spaces and indent the first line too.
+ """
+ indention = u' ' * width
+ rv = (u'\n' + indention).join(s.splitlines())
+ if indentfirst:
+ rv = indention + rv
+ return rv
+
+
+def do_truncate(s, length=255, killwords=False, end='...'):
+ """Return a truncated copy of the string. The length is specified
+ with the first parameter which defaults to ``255``. If the second
+ parameter is ``true`` the filter will cut the text at length. Otherwise
+ it will try to save the last word. If the text was in fact
+ truncated it will append an ellipsis sign (``"..."``). If you want a
+ different ellipsis sign than ``"..."`` you can specify it using the
+ third parameter.
+
+ .. sourcecode jinja::
+
+ {{ mytext|truncate(300, false, '&raquo;') }}
+ truncate mytext to 300 chars, don't split up words, use a
+ right pointing double arrow as ellipsis sign.
+ """
+ if len(s) <= length:
+ return s
+ elif killwords:
+ return s[:length] + end
+ words = s.split(' ')
+ result = []
+ m = 0
+ for word in words:
+ m += len(word) + 1
+ if m > length:
+ break
+ result.append(word)
+ result.append(end)
+ return u' '.join(result)
+
+@environmentfilter
+def do_wordwrap(environment, s, width=79, break_long_words=True):
+ """
+ Return a copy of the string passed to the filter wrapped after
+ ``79`` characters. You can override this default using the first
+ parameter. If you set the second parameter to `false` Jinja will not
+ split words apart if they are longer than `width`.
+ """
+ import textwrap
+ return environment.newline_sequence.join(textwrap.wrap(s, width=width, expand_tabs=False,
+ replace_whitespace=False,
+ break_long_words=break_long_words))
+
+
+def do_wordcount(s):
+ """Count the words in that string."""
+ return len(_word_re.findall(s))
+
+
+def do_int(value, default=0):
+ """Convert the value into an integer. If the
+ conversion doesn't work it will return ``0``. You can
+ override this default using the first parameter.
+ """
+ try:
+ return int(value)
+ except (TypeError, ValueError):
+ # this quirk is necessary so that "42.23"|int gives 42.
+ try:
+ return int(float(value))
+ except (TypeError, ValueError):
+ return default
+
+
+def do_float(value, default=0.0):
+ """Convert the value into a floating point number. If the
+ conversion doesn't work it will return ``0.0``. You can
+ override this default using the first parameter.
+ """
+ try:
+ return float(value)
+ except (TypeError, ValueError):
+ return default
+
+
+def do_format(value, *args, **kwargs):
+ """
+ Apply python string formatting on an object:
+
+ .. sourcecode:: jinja
+
+ {{ "%s - %s"|format("Hello?", "Foo!") }}
+ -> Hello? - Foo!
+ """
+ if args and kwargs:
+ raise FilterArgumentError('can\'t handle positional and keyword '
+ 'arguments at the same time')
+ return soft_unicode(value) % (kwargs or args)
+
+
+def do_trim(value):
+ """Strip leading and trailing whitespace."""
+ return soft_unicode(value).strip()
+
+
+def do_striptags(value):
+ """Strip SGML/XML tags and replace adjacent whitespace by one space.
+ """
+ if hasattr(value, '__html__'):
+ value = value.__html__()
+ return Markup(unicode(value)).striptags()
+
+
+def do_slice(value, slices, fill_with=None):
+ """Slice an iterator and return a list of lists containing
+ those items. Useful if you want to create a div containing
+ three ul tags that represent columns:
+
+ .. sourcecode:: html+jinja
+
+ <div class="columwrapper">
+ {%- for column in items|slice(3) %}
+ <ul class="column-{{ loop.index }}">
+ {%- for item in column %}
+ <li>{{ item }}</li>
+ {%- endfor %}
+ </ul>
+ {%- endfor %}
+ </div>
+
+ If you pass it a second argument it's used to fill missing
+ values on the last iteration.
+ """
+ seq = list(value)
+ length = len(seq)
+ items_per_slice = length // slices
+ slices_with_extra = length % slices
+ offset = 0
+ for slice_number in xrange(slices):
+ start = offset + slice_number * items_per_slice
+ if slice_number < slices_with_extra:
+ offset += 1
+ end = offset + (slice_number + 1) * items_per_slice
+ tmp = seq[start:end]
+ if fill_with is not None and slice_number >= slices_with_extra:
+ tmp.append(fill_with)
+ yield tmp
+
+
+def do_batch(value, linecount, fill_with=None):
+ """
+ A filter that batches items. It works pretty much like `slice`
+ just the other way round. It returns a list of lists with the
+ given number of items. If you provide a second parameter this
+ is used to fill missing items. See this example:
+
+ .. sourcecode:: html+jinja
+
+ <table>
+ {%- for row in items|batch(3, '&nbsp;') %}
+ <tr>
+ {%- for column in row %}
+ <td>{{ column }}</td>
+ {%- endfor %}
+ </tr>
+ {%- endfor %}
+ </table>
+ """
+ result = []
+ tmp = []
+ for item in value:
+ if len(tmp) == linecount:
+ yield tmp
+ tmp = []
+ tmp.append(item)
+ if tmp:
+ if fill_with is not None and len(tmp) < linecount:
+ tmp += [fill_with] * (linecount - len(tmp))
+ yield tmp
+
+
+def do_round(value, precision=0, method='common'):
+ """Round the number to a given precision. The first
+ parameter specifies the precision (default is ``0``), the
+ second the rounding method:
+
+ - ``'common'`` rounds either up or down
+ - ``'ceil'`` always rounds up
+ - ``'floor'`` always rounds down
+
+ If you don't specify a method ``'common'`` is used.
+
+ .. sourcecode:: jinja
+
+ {{ 42.55|round }}
+ -> 43.0
+ {{ 42.55|round(1, 'floor') }}
+ -> 42.5
+
+ Note that even if rounded to 0 precision, a float is returned. If
+ you need a real integer, pipe it through `int`:
+
+ .. sourcecode:: jinja
+
+ {{ 42.55|round|int }}
+ -> 43
+ """
+ if not method in ('common', 'ceil', 'floor'):
+ raise FilterArgumentError('method must be common, ceil or floor')
+ if method == 'common':
+ return round(value, precision)
+ func = getattr(math, method)
+ return func(value * (10 ** precision)) / (10 ** precision)
+
+
+@environmentfilter
+def do_groupby(environment, value, attribute):
+ """Group a sequence of objects by a common attribute.
+
+ If you for example have a list of dicts or objects that represent persons
+ with `gender`, `first_name` and `last_name` attributes and you want to
+ group all users by genders you can do something like the following
+ snippet:
+
+ .. sourcecode:: html+jinja
+
+ <ul>
+ {% for group in persons|groupby('gender') %}
+ <li>{{ group.grouper }}<ul>
+ {% for person in group.list %}
+ <li>{{ person.first_name }} {{ person.last_name }}</li>
+ {% endfor %}</ul></li>
+ {% endfor %}
+ </ul>
+
+ Additionally it's possible to use tuple unpacking for the grouper and
+ list:
+
+ .. sourcecode:: html+jinja
+
+ <ul>
+ {% for grouper, list in persons|groupby('gender') %}
+ ...
+ {% endfor %}
+ </ul>
+
+ As you can see the item we're grouping by is stored in the `grouper`
+ attribute and the `list` contains all the objects that have this grouper
+ in common.
+
+ .. versionchanged:: 2.6
+ It's now possible to use dotted notation to group by the child
+ attribute of another attribute.
+ """
+ expr = make_attrgetter(environment, attribute)
+ return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
+
+
+class _GroupTuple(tuple):
+ __slots__ = ()
+ grouper = property(itemgetter(0))
+ list = property(itemgetter(1))
+
+ def __new__(cls, (key, value)):
+ return tuple.__new__(cls, (key, list(value)))
+
+
+@environmentfilter
+def do_sum(environment, iterable, attribute=None, start=0):
+ """Returns the sum of a sequence of numbers plus the value of parameter
+ 'start' (which defaults to 0). When the sequence is empty it returns
+ start.
+
+ It is also possible to sum up only certain attributes:
+
+ .. sourcecode:: jinja
+
+ Total: {{ items|sum(attribute='price') }}
+
+ .. versionchanged:: 2.6
+ The `attribute` parameter was added to allow suming up over
+ attributes. Also the `start` parameter was moved on to the right.
+ """
+ if attribute is not None:
+ iterable = imap(make_attrgetter(environment, attribute), iterable)
+ return sum(iterable, start)
+
+
+def do_list(value):
+ """Convert the value into a list. If it was a string the returned list
+ will be a list of characters.
+ """
+ return list(value)
+
+
+def do_mark_safe(value):
+ """Mark the value as safe which means that in an environment with automatic
+ escaping enabled this variable will not be escaped.
+ """
+ return Markup(value)
+
+
+def do_mark_unsafe(value):
+ """Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
+ return unicode(value)
+
+
+def do_reverse(value):
+ """Reverse the object or return an iterator the iterates over it the other
+ way round.
+ """
+ if isinstance(value, basestring):
+ return value[::-1]
+ try:
+ return reversed(value)
+ except TypeError:
+ try:
+ rv = list(value)
+ rv.reverse()
+ return rv
+ except TypeError:
+ raise FilterArgumentError('argument must be iterable')
+
+
+@environmentfilter
+def do_attr(environment, obj, name):
+ """Get an attribute of an object. ``foo|attr("bar")`` works like
+ ``foo["bar"]`` just that always an attribute is returned and items are not
+ looked up.
+
+ See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
+ """
+ try:
+ name = str(name)
+ except UnicodeError:
+ pass
+ else:
+ try:
+ value = getattr(obj, name)
+ except AttributeError:
+ pass
+ else:
+ if environment.sandboxed and not \
+ environment.is_safe_attribute(obj, name, value):
+ return environment.unsafe_undefined(obj, name)
+ return value
+ return environment.undefined(obj=obj, name=name)
+
+
+FILTERS = {
+ 'attr': do_attr,
+ 'replace': do_replace,
+ 'upper': do_upper,
+ 'lower': do_lower,
+ 'escape': escape,
+ 'e': escape,
+ 'forceescape': do_forceescape,
+ 'capitalize': do_capitalize,
+ 'title': do_title,
+ 'default': do_default,
+ 'd': do_default,
+ 'join': do_join,
+ 'count': len,
+ 'dictsort': do_dictsort,
+ 'sort': do_sort,
+ 'length': len,
+ 'reverse': do_reverse,
+ 'center': do_center,
+ 'indent': do_indent,
+ 'title': do_title,
+ 'capitalize': do_capitalize,
+ 'first': do_first,
+ 'last': do_last,
+ 'random': do_random,
+ 'filesizeformat': do_filesizeformat,
+ 'pprint': do_pprint,
+ 'truncate': do_truncate,
+ 'wordwrap': do_wordwrap,
+ 'wordcount': do_wordcount,
+ 'int': do_int,
+ 'float': do_float,
+ 'string': soft_unicode,
+ 'list': do_list,
+ 'urlize': do_urlize,
+ 'format': do_format,
+ 'trim': do_trim,
+ 'striptags': do_striptags,
+ 'slice': do_slice,
+ 'batch': do_batch,
+ 'sum': do_sum,
+ 'abs': abs,
+ 'round': do_round,
+ 'groupby': do_groupby,
+ 'safe': do_mark_safe,
+ 'xmlattr': do_xmlattr
+}
diff --git a/websdk/jinja2/lexer.py b/websdk/jinja2/lexer.py
new file mode 100644
index 0000000..69865d0
--- /dev/null
+++ b/websdk/jinja2/lexer.py
@@ -0,0 +1,681 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.lexer
+ ~~~~~~~~~~~~
+
+ This module implements a Jinja / Python combination lexer. The
+ `Lexer` class provided by this module is used to do some preprocessing
+ for Jinja.
+
+ On the one hand it filters out invalid operators like the bitshift
+ operators we don't allow in templates. On the other hand it separates
+ template code and python code in expressions.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+from operator import itemgetter
+from collections import deque
+from jinja2.exceptions import TemplateSyntaxError
+from jinja2.utils import LRUCache, next
+
+
+# cache for the lexers. Exists in order to be able to have multiple
+# environments with the same lexer
+_lexer_cache = LRUCache(50)
+
+# static regular expressions
+whitespace_re = re.compile(r'\s+', re.U)
+string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
+ r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
+integer_re = re.compile(r'\d+')
+
+# we use the unicode identifier rule if this python version is able
+# to handle unicode identifiers, otherwise the standard ASCII one.
+try:
+ compile('föö', '<unknown>', 'eval')
+except SyntaxError:
+ name_re = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b')
+else:
+ from jinja2 import _stringdefs
+ name_re = re.compile(r'[%s][%s]*' % (_stringdefs.xid_start,
+ _stringdefs.xid_continue))
+
+float_re = re.compile(r'(?<!\.)\d+\.\d+')
+newline_re = re.compile(r'(\r\n|\r|\n)')
+
+# internal the tokens and keep references to them
+TOKEN_ADD = intern('add')
+TOKEN_ASSIGN = intern('assign')
+TOKEN_COLON = intern('colon')
+TOKEN_COMMA = intern('comma')
+TOKEN_DIV = intern('div')
+TOKEN_DOT = intern('dot')
+TOKEN_EQ = intern('eq')
+TOKEN_FLOORDIV = intern('floordiv')
+TOKEN_GT = intern('gt')
+TOKEN_GTEQ = intern('gteq')
+TOKEN_LBRACE = intern('lbrace')
+TOKEN_LBRACKET = intern('lbracket')
+TOKEN_LPAREN = intern('lparen')
+TOKEN_LT = intern('lt')
+TOKEN_LTEQ = intern('lteq')
+TOKEN_MOD = intern('mod')
+TOKEN_MUL = intern('mul')
+TOKEN_NE = intern('ne')
+TOKEN_PIPE = intern('pipe')
+TOKEN_POW = intern('pow')
+TOKEN_RBRACE = intern('rbrace')
+TOKEN_RBRACKET = intern('rbracket')
+TOKEN_RPAREN = intern('rparen')
+TOKEN_SEMICOLON = intern('semicolon')
+TOKEN_SUB = intern('sub')
+TOKEN_TILDE = intern('tilde')
+TOKEN_WHITESPACE = intern('whitespace')
+TOKEN_FLOAT = intern('float')
+TOKEN_INTEGER = intern('integer')
+TOKEN_NAME = intern('name')
+TOKEN_STRING = intern('string')
+TOKEN_OPERATOR = intern('operator')
+TOKEN_BLOCK_BEGIN = intern('block_begin')
+TOKEN_BLOCK_END = intern('block_end')
+TOKEN_VARIABLE_BEGIN = intern('variable_begin')
+TOKEN_VARIABLE_END = intern('variable_end')
+TOKEN_RAW_BEGIN = intern('raw_begin')
+TOKEN_RAW_END = intern('raw_end')
+TOKEN_COMMENT_BEGIN = intern('comment_begin')
+TOKEN_COMMENT_END = intern('comment_end')
+TOKEN_COMMENT = intern('comment')
+TOKEN_LINESTATEMENT_BEGIN = intern('linestatement_begin')
+TOKEN_LINESTATEMENT_END = intern('linestatement_end')
+TOKEN_LINECOMMENT_BEGIN = intern('linecomment_begin')
+TOKEN_LINECOMMENT_END = intern('linecomment_end')
+TOKEN_LINECOMMENT = intern('linecomment')
+TOKEN_DATA = intern('data')
+TOKEN_INITIAL = intern('initial')
+TOKEN_EOF = intern('eof')
+
+# bind operators to token types
+operators = {
+ '+': TOKEN_ADD,
+ '-': TOKEN_SUB,
+ '/': TOKEN_DIV,
+ '//': TOKEN_FLOORDIV,
+ '*': TOKEN_MUL,
+ '%': TOKEN_MOD,
+ '**': TOKEN_POW,
+ '~': TOKEN_TILDE,
+ '[': TOKEN_LBRACKET,
+ ']': TOKEN_RBRACKET,
+ '(': TOKEN_LPAREN,
+ ')': TOKEN_RPAREN,
+ '{': TOKEN_LBRACE,
+ '}': TOKEN_RBRACE,
+ '==': TOKEN_EQ,
+ '!=': TOKEN_NE,
+ '>': TOKEN_GT,
+ '>=': TOKEN_GTEQ,
+ '<': TOKEN_LT,
+ '<=': TOKEN_LTEQ,
+ '=': TOKEN_ASSIGN,
+ '.': TOKEN_DOT,
+ ':': TOKEN_COLON,
+ '|': TOKEN_PIPE,
+ ',': TOKEN_COMMA,
+ ';': TOKEN_SEMICOLON
+}
+
+reverse_operators = dict([(v, k) for k, v in operators.iteritems()])
+assert len(operators) == len(reverse_operators), 'operators dropped'
+operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in
+ sorted(operators, key=lambda x: -len(x))))
+
+ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT,
+ TOKEN_COMMENT_END, TOKEN_WHITESPACE,
+ TOKEN_WHITESPACE, TOKEN_LINECOMMENT_BEGIN,
+ TOKEN_LINECOMMENT_END, TOKEN_LINECOMMENT])
+ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA,
+ TOKEN_COMMENT, TOKEN_LINECOMMENT])
+
+
+def _describe_token_type(token_type):
+ if token_type in reverse_operators:
+ return reverse_operators[token_type]
+ return {
+ TOKEN_COMMENT_BEGIN: 'begin of comment',
+ TOKEN_COMMENT_END: 'end of comment',
+ TOKEN_COMMENT: 'comment',
+ TOKEN_LINECOMMENT: 'comment',
+ TOKEN_BLOCK_BEGIN: 'begin of statement block',
+ TOKEN_BLOCK_END: 'end of statement block',
+ TOKEN_VARIABLE_BEGIN: 'begin of print statement',
+ TOKEN_VARIABLE_END: 'end of print statement',
+ TOKEN_LINESTATEMENT_BEGIN: 'begin of line statement',
+ TOKEN_LINESTATEMENT_END: 'end of line statement',
+ TOKEN_DATA: 'template data / text',
+ TOKEN_EOF: 'end of template'
+ }.get(token_type, token_type)
+
+
+def describe_token(token):
+ """Returns a description of the token."""
+ if token.type == 'name':
+ return token.value
+ return _describe_token_type(token.type)
+
+
+def describe_token_expr(expr):
+ """Like `describe_token` but for token expressions."""
+ if ':' in expr:
+ type, value = expr.split(':', 1)
+ if type == 'name':
+ return value
+ else:
+ type = expr
+ return _describe_token_type(type)
+
+
+def count_newlines(value):
+ """Count the number of newline characters in the string. This is
+ useful for extensions that filter a stream.
+ """
+ return len(newline_re.findall(value))
+
+
+def compile_rules(environment):
+ """Compiles all the rules from the environment into a list of rules."""
+ e = re.escape
+ rules = [
+ (len(environment.comment_start_string), 'comment',
+ e(environment.comment_start_string)),
+ (len(environment.block_start_string), 'block',
+ e(environment.block_start_string)),
+ (len(environment.variable_start_string), 'variable',
+ e(environment.variable_start_string))
+ ]
+
+ if environment.line_statement_prefix is not None:
+ rules.append((len(environment.line_statement_prefix), 'linestatement',
+ r'^\s*' + e(environment.line_statement_prefix)))
+ if environment.line_comment_prefix is not None:
+ rules.append((len(environment.line_comment_prefix), 'linecomment',
+ r'(?:^|(?<=\S))[^\S\r\n]*' +
+ e(environment.line_comment_prefix)))
+
+ return [x[1:] for x in sorted(rules, reverse=True)]
+
+
+class Failure(object):
+ """Class that raises a `TemplateSyntaxError` if called.
+ Used by the `Lexer` to specify known errors.
+ """
+
+ def __init__(self, message, cls=TemplateSyntaxError):
+ self.message = message
+ self.error_class = cls
+
+ def __call__(self, lineno, filename):
+ raise self.error_class(self.message, lineno, filename)
+
+
+class Token(tuple):
+ """Token class."""
+ __slots__ = ()
+ lineno, type, value = (property(itemgetter(x)) for x in range(3))
+
+ def __new__(cls, lineno, type, value):
+ return tuple.__new__(cls, (lineno, intern(str(type)), value))
+
+ def __str__(self):
+ if self.type in reverse_operators:
+ return reverse_operators[self.type]
+ elif self.type == 'name':
+ return self.value
+ return self.type
+
+ def test(self, expr):
+ """Test a token against a token expression. This can either be a
+ token type or ``'token_type:token_value'``. This can only test
+ against string values and types.
+ """
+ # here we do a regular string equality check as test_any is usually
+ # passed an iterable of not interned strings.
+ if self.type == expr:
+ return True
+ elif ':' in expr:
+ return expr.split(':', 1) == [self.type, self.value]
+ return False
+
+ def test_any(self, *iterable):
+ """Test against multiple token expressions."""
+ for expr in iterable:
+ if self.test(expr):
+ return True
+ return False
+
+ def __repr__(self):
+ return 'Token(%r, %r, %r)' % (
+ self.lineno,
+ self.type,
+ self.value
+ )
+
+
+class TokenStreamIterator(object):
+ """The iterator for tokenstreams. Iterate over the stream
+ until the eof token is reached.
+ """
+
+ def __init__(self, stream):
+ self.stream = stream
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ token = self.stream.current
+ if token.type is TOKEN_EOF:
+ self.stream.close()
+ raise StopIteration()
+ next(self.stream)
+ return token
+
+
+class TokenStream(object):
+ """A token stream is an iterable that yields :class:`Token`\s. The
+ parser however does not iterate over it but calls :meth:`next` to go
+ one token ahead. The current active token is stored as :attr:`current`.
+ """
+
+ def __init__(self, generator, name, filename):
+ self._next = iter(generator).next
+ self._pushed = deque()
+ self.name = name
+ self.filename = filename
+ self.closed = False
+ self.current = Token(1, TOKEN_INITIAL, '')
+ next(self)
+
+ def __iter__(self):
+ return TokenStreamIterator(self)
+
+ def __nonzero__(self):
+ return bool(self._pushed) or self.current.type is not TOKEN_EOF
+
+ eos = property(lambda x: not x, doc="Are we at the end of the stream?")
+
+ def push(self, token):
+ """Push a token back to the stream."""
+ self._pushed.append(token)
+
+ def look(self):
+ """Look at the next token."""
+ old_token = next(self)
+ result = self.current
+ self.push(result)
+ self.current = old_token
+ return result
+
+ def skip(self, n=1):
+ """Got n tokens ahead."""
+ for x in xrange(n):
+ next(self)
+
+ def next_if(self, expr):
+ """Perform the token test and return the token if it matched.
+ Otherwise the return value is `None`.
+ """
+ if self.current.test(expr):
+ return next(self)
+
+ def skip_if(self, expr):
+ """Like :meth:`next_if` but only returns `True` or `False`."""
+ return self.next_if(expr) is not None
+
+ def next(self):
+ """Go one token ahead and return the old one"""
+ rv = self.current
+ if self._pushed:
+ self.current = self._pushed.popleft()
+ elif self.current.type is not TOKEN_EOF:
+ try:
+ self.current = self._next()
+ except StopIteration:
+ self.close()
+ return rv
+
+ def close(self):
+ """Close the stream."""
+ self.current = Token(self.current.lineno, TOKEN_EOF, '')
+ self._next = None
+ self.closed = True
+
+ def expect(self, expr):
+ """Expect a given token type and return it. This accepts the same
+ argument as :meth:`jinja2.lexer.Token.test`.
+ """
+ if not self.current.test(expr):
+ expr = describe_token_expr(expr)
+ if self.current.type is TOKEN_EOF:
+ raise TemplateSyntaxError('unexpected end of template, '
+ 'expected %r.' % expr,
+ self.current.lineno,
+ self.name, self.filename)
+ raise TemplateSyntaxError("expected token %r, got %r" %
+ (expr, describe_token(self.current)),
+ self.current.lineno,
+ self.name, self.filename)
+ try:
+ return self.current
+ finally:
+ next(self)
+
+
+def get_lexer(environment):
+ """Return a lexer which is probably cached."""
+ key = (environment.block_start_string,
+ environment.block_end_string,
+ environment.variable_start_string,
+ environment.variable_end_string,
+ environment.comment_start_string,
+ environment.comment_end_string,
+ environment.line_statement_prefix,
+ environment.line_comment_prefix,
+ environment.trim_blocks,
+ environment.newline_sequence)
+ lexer = _lexer_cache.get(key)
+ if lexer is None:
+ lexer = Lexer(environment)
+ _lexer_cache[key] = lexer
+ return lexer
+
+
+class Lexer(object):
+ """Class that implements a lexer for a given environment. Automatically
+ created by the environment class, usually you don't have to do that.
+
+ Note that the lexer is not automatically bound to an environment.
+ Multiple environments can share the same lexer.
+ """
+
+ def __init__(self, environment):
+ # shortcuts
+ c = lambda x: re.compile(x, re.M | re.S)
+ e = re.escape
+
+ # lexing rules for tags
+ tag_rules = [
+ (whitespace_re, TOKEN_WHITESPACE, None),
+ (float_re, TOKEN_FLOAT, None),
+ (integer_re, TOKEN_INTEGER, None),
+ (name_re, TOKEN_NAME, None),
+ (string_re, TOKEN_STRING, None),
+ (operator_re, TOKEN_OPERATOR, None)
+ ]
+
+ # assemble the root lexing rule. because "|" is ungreedy
+ # we have to sort by length so that the lexer continues working
+ # as expected when we have parsing rules like <% for block and
+ # <%= for variables. (if someone wants asp like syntax)
+ # variables are just part of the rules if variable processing
+ # is required.
+ root_tag_rules = compile_rules(environment)
+
+ # block suffix if trimming is enabled
+ block_suffix_re = environment.trim_blocks and '\\n?' or ''
+
+ self.newline_sequence = environment.newline_sequence
+
+ # global lexing rules
+ self.rules = {
+ 'root': [
+ # directives
+ (c('(.*?)(?:%s)' % '|'.join(
+ [r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % (
+ e(environment.block_start_string),
+ e(environment.block_start_string),
+ e(environment.block_end_string),
+ e(environment.block_end_string)
+ )] + [
+ r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, r)
+ for n, r in root_tag_rules
+ ])), (TOKEN_DATA, '#bygroup'), '#bygroup'),
+ # data
+ (c('.+'), TOKEN_DATA, None)
+ ],
+ # comments
+ TOKEN_COMMENT_BEGIN: [
+ (c(r'(.*?)((?:\-%s\s*|%s)%s)' % (
+ e(environment.comment_end_string),
+ e(environment.comment_end_string),
+ block_suffix_re
+ )), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'),
+ (c('(.)'), (Failure('Missing end of comment tag'),), None)
+ ],
+ # blocks
+ TOKEN_BLOCK_BEGIN: [
+ (c('(?:\-%s\s*|%s)%s' % (
+ e(environment.block_end_string),
+ e(environment.block_end_string),
+ block_suffix_re
+ )), TOKEN_BLOCK_END, '#pop'),
+ ] + tag_rules,
+ # variables
+ TOKEN_VARIABLE_BEGIN: [
+ (c('\-%s\s*|%s' % (
+ e(environment.variable_end_string),
+ e(environment.variable_end_string)
+ )), TOKEN_VARIABLE_END, '#pop')
+ ] + tag_rules,
+ # raw block
+ TOKEN_RAW_BEGIN: [
+ (c('(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % (
+ e(environment.block_start_string),
+ e(environment.block_start_string),
+ e(environment.block_end_string),
+ e(environment.block_end_string),
+ block_suffix_re
+ )), (TOKEN_DATA, TOKEN_RAW_END), '#pop'),
+ (c('(.)'), (Failure('Missing end of raw directive'),), None)
+ ],
+ # line statements
+ TOKEN_LINESTATEMENT_BEGIN: [
+ (c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop')
+ ] + tag_rules,
+ # line comments
+ TOKEN_LINECOMMENT_BEGIN: [
+ (c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT,
+ TOKEN_LINECOMMENT_END), '#pop')
+ ]
+ }
+
+ def _normalize_newlines(self, value):
+ """Called for strings and template data to normalize it to unicode."""
+ return newline_re.sub(self.newline_sequence, value)
+
+ def tokenize(self, source, name=None, filename=None, state=None):
+ """Calls tokeniter + tokenize and wraps it in a token stream.
+ """
+ stream = self.tokeniter(source, name, filename, state)
+ return TokenStream(self.wrap(stream, name, filename), name, filename)
+
+ def wrap(self, stream, name=None, filename=None):
+ """This is called with the stream as returned by `tokenize` and wraps
+ every token in a :class:`Token` and converts the value.
+ """
+ for lineno, token, value in stream:
+ if token in ignored_tokens:
+ continue
+ elif token == 'linestatement_begin':
+ token = 'block_begin'
+ elif token == 'linestatement_end':
+ token = 'block_end'
+ # we are not interested in those tokens in the parser
+ elif token in ('raw_begin', 'raw_end'):
+ continue
+ elif token == 'data':
+ value = self._normalize_newlines(value)
+ elif token == 'keyword':
+ token = value
+ elif token == 'name':
+ value = str(value)
+ elif token == 'string':
+ # try to unescape string
+ try:
+ value = self._normalize_newlines(value[1:-1]) \
+ .encode('ascii', 'backslashreplace') \
+ .decode('unicode-escape')
+ except Exception, e:
+ msg = str(e).split(':')[-1].strip()
+ raise TemplateSyntaxError(msg, lineno, name, filename)
+ # if we can express it as bytestring (ascii only)
+ # we do that for support of semi broken APIs
+ # as datetime.datetime.strftime. On python 3 this
+ # call becomes a noop thanks to 2to3
+ try:
+ value = str(value)
+ except UnicodeError:
+ pass
+ elif token == 'integer':
+ value = int(value)
+ elif token == 'float':
+ value = float(value)
+ elif token == 'operator':
+ token = operators[value]
+ yield Token(lineno, token, value)
+
+ def tokeniter(self, source, name, filename=None, state=None):
+ """This method tokenizes the text and returns the tokens in a
+ generator. Use this method if you just want to tokenize a template.
+ """
+ source = '\n'.join(unicode(source).splitlines())
+ pos = 0
+ lineno = 1
+ stack = ['root']
+ if state is not None and state != 'root':
+ assert state in ('variable', 'block'), 'invalid state'
+ stack.append(state + '_begin')
+ else:
+ state = 'root'
+ statetokens = self.rules[stack[-1]]
+ source_length = len(source)
+
+ balancing_stack = []
+
+ while 1:
+ # tokenizer loop
+ for regex, tokens, new_state in statetokens:
+ m = regex.match(source, pos)
+ # if no match we try again with the next rule
+ if m is None:
+ continue
+
+ # we only match blocks and variables if braces / parentheses
+ # are balanced. continue parsing with the lower rule which
+ # is the operator rule. do this only if the end tags look
+ # like operators
+ if balancing_stack and \
+ tokens in ('variable_end', 'block_end',
+ 'linestatement_end'):
+ continue
+
+ # tuples support more options
+ if isinstance(tokens, tuple):
+ for idx, token in enumerate(tokens):
+ # failure group
+ if token.__class__ is Failure:
+ raise token(lineno, filename)
+ # bygroup is a bit more complex, in that case we
+ # yield for the current token the first named
+ # group that matched
+ elif token == '#bygroup':
+ for key, value in m.groupdict().iteritems():
+ if value is not None:
+ yield lineno, key, value
+ lineno += value.count('\n')
+ break
+ else:
+ raise RuntimeError('%r wanted to resolve '
+ 'the token dynamically'
+ ' but no group matched'
+ % regex)
+ # normal group
+ else:
+ data = m.group(idx + 1)
+ if data or token not in ignore_if_empty:
+ yield lineno, token, data
+ lineno += data.count('\n')
+
+ # strings as token just are yielded as it.
+ else:
+ data = m.group()
+ # update brace/parentheses balance
+ if tokens == 'operator':
+ if data == '{':
+ balancing_stack.append('}')
+ elif data == '(':
+ balancing_stack.append(')')
+ elif data == '[':
+ balancing_stack.append(']')
+ elif data in ('}', ')', ']'):
+ if not balancing_stack:
+ raise TemplateSyntaxError('unexpected \'%s\'' %
+ data, lineno, name,
+ filename)
+ expected_op = balancing_stack.pop()
+ if expected_op != data:
+ raise TemplateSyntaxError('unexpected \'%s\', '
+ 'expected \'%s\'' %
+ (data, expected_op),
+ lineno, name,
+ filename)
+ # yield items
+ if data or tokens not in ignore_if_empty:
+ yield lineno, tokens, data
+ lineno += data.count('\n')
+
+ # fetch new position into new variable so that we can check
+ # if there is a internal parsing error which would result
+ # in an infinite loop
+ pos2 = m.end()
+
+ # handle state changes
+ if new_state is not None:
+ # remove the uppermost state
+ if new_state == '#pop':
+ stack.pop()
+ # resolve the new state by group checking
+ elif new_state == '#bygroup':
+ for key, value in m.groupdict().iteritems():
+ if value is not None:
+ stack.append(key)
+ break
+ else:
+ raise RuntimeError('%r wanted to resolve the '
+ 'new state dynamically but'
+ ' no group matched' %
+ regex)
+ # direct state name given
+ else:
+ stack.append(new_state)
+ statetokens = self.rules[stack[-1]]
+ # we are still at the same position and no stack change.
+ # this means a loop without break condition, avoid that and
+ # raise error
+ elif pos2 == pos:
+ raise RuntimeError('%r yielded empty string without '
+ 'stack change' % regex)
+ # publish new function and start again
+ pos = pos2
+ break
+ # if loop terminated without break we haven't found a single match
+ # either we are at the end of the file or we have a problem
+ else:
+ # end of text
+ if pos >= source_length:
+ return
+ # something went wrong
+ raise TemplateSyntaxError('unexpected char %r at %d' %
+ (source[pos], pos), lineno,
+ name, filename)
diff --git a/websdk/jinja2/loaders.py b/websdk/jinja2/loaders.py
new file mode 100644
index 0000000..c90bbe7
--- /dev/null
+++ b/websdk/jinja2/loaders.py
@@ -0,0 +1,473 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.loaders
+ ~~~~~~~~~~~~~~
+
+ Jinja loader classes.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+import weakref
+from types import ModuleType
+from os import path
+try:
+ from hashlib import sha1
+except ImportError:
+ from sha import new as sha1
+from jinja2.exceptions import TemplateNotFound
+from jinja2.utils import LRUCache, open_if_exists, internalcode
+
+
+def split_template_path(template):
+ """Split a path into segments and perform a sanity check. If it detects
+ '..' in the path it will raise a `TemplateNotFound` error.
+ """
+ pieces = []
+ for piece in template.split('/'):
+ if path.sep in piece \
+ or (path.altsep and path.altsep in piece) or \
+ piece == path.pardir:
+ raise TemplateNotFound(template)
+ elif piece and piece != '.':
+ pieces.append(piece)
+ return pieces
+
+
+class BaseLoader(object):
+ """Baseclass for all loaders. Subclass this and override `get_source` to
+ implement a custom loading mechanism. The environment provides a
+ `get_template` method that calls the loader's `load` method to get the
+ :class:`Template` object.
+
+ A very basic example for a loader that looks up templates on the file
+ system could look like this::
+
+ from jinja2 import BaseLoader, TemplateNotFound
+ from os.path import join, exists, getmtime
+
+ class MyLoader(BaseLoader):
+
+ def __init__(self, path):
+ self.path = path
+
+ def get_source(self, environment, template):
+ path = join(self.path, template)
+ if not exists(path):
+ raise TemplateNotFound(template)
+ mtime = getmtime(path)
+ with file(path) as f:
+ source = f.read().decode('utf-8')
+ return source, path, lambda: mtime == getmtime(path)
+ """
+
+ #: if set to `False` it indicates that the loader cannot provide access
+ #: to the source of templates.
+ #:
+ #: .. versionadded:: 2.4
+ has_source_access = True
+
+ def get_source(self, environment, template):
+ """Get the template source, filename and reload helper for a template.
+ It's passed the environment and template name and has to return a
+ tuple in the form ``(source, filename, uptodate)`` or raise a
+ `TemplateNotFound` error if it can't locate the template.
+
+ The source part of the returned tuple must be the source of the
+ template as unicode string or a ASCII bytestring. The filename should
+ be the name of the file on the filesystem if it was loaded from there,
+ otherwise `None`. The filename is used by python for the tracebacks
+ if no loader extension is used.
+
+ The last item in the tuple is the `uptodate` function. If auto
+ reloading is enabled it's always called to check if the template
+ changed. No arguments are passed so the function must store the
+ old state somewhere (for example in a closure). If it returns `False`
+ the template will be reloaded.
+ """
+ if not self.has_source_access:
+ raise RuntimeError('%s cannot provide access to the source' %
+ self.__class__.__name__)
+ raise TemplateNotFound(template)
+
+ def list_templates(self):
+ """Iterates over all templates. If the loader does not support that
+ it should raise a :exc:`TypeError` which is the default behavior.
+ """
+ raise TypeError('this loader cannot iterate over all templates')
+
+ @internalcode
+ def load(self, environment, name, globals=None):
+ """Loads a template. This method looks up the template in the cache
+ or loads one by calling :meth:`get_source`. Subclasses should not
+ override this method as loaders working on collections of other
+ loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`)
+ will not call this method but `get_source` directly.
+ """
+ code = None
+ if globals is None:
+ globals = {}
+
+ # first we try to get the source for this template together
+ # with the filename and the uptodate function.
+ source, filename, uptodate = self.get_source(environment, name)
+
+ # try to load the code from the bytecode cache if there is a
+ # bytecode cache configured.
+ bcc = environment.bytecode_cache
+ if bcc is not None:
+ bucket = bcc.get_bucket(environment, name, filename, source)
+ code = bucket.code
+
+ # if we don't have code so far (not cached, no longer up to
+ # date) etc. we compile the template
+ if code is None:
+ code = environment.compile(source, name, filename)
+
+ # if the bytecode cache is available and the bucket doesn't
+ # have a code so far, we give the bucket the new code and put
+ # it back to the bytecode cache.
+ if bcc is not None and bucket.code is None:
+ bucket.code = code
+ bcc.set_bucket(bucket)
+
+ return environment.template_class.from_code(environment, code,
+ globals, uptodate)
+
+
+class FileSystemLoader(BaseLoader):
+ """Loads templates from the file system. This loader can find templates
+ in folders on the file system and is the preferred way to load them.
+
+ The loader takes the path to the templates as string, or if multiple
+ locations are wanted a list of them which is then looked up in the
+ given order:
+
+ >>> loader = FileSystemLoader('/path/to/templates')
+ >>> loader = FileSystemLoader(['/path/to/templates', '/other/path'])
+
+ Per default the template encoding is ``'utf-8'`` which can be changed
+ by setting the `encoding` parameter to something else.
+ """
+
+ def __init__(self, searchpath, encoding='utf-8'):
+ if isinstance(searchpath, basestring):
+ searchpath = [searchpath]
+ self.searchpath = list(searchpath)
+ self.encoding = encoding
+
+ def get_source(self, environment, template):
+ pieces = split_template_path(template)
+ for searchpath in self.searchpath:
+ filename = path.join(searchpath, *pieces)
+ f = open_if_exists(filename)
+ if f is None:
+ continue
+ try:
+ contents = f.read().decode(self.encoding)
+ finally:
+ f.close()
+
+ mtime = path.getmtime(filename)
+ def uptodate():
+ try:
+ return path.getmtime(filename) == mtime
+ except OSError:
+ return False
+ return contents, filename, uptodate
+ raise TemplateNotFound(template)
+
+ def list_templates(self):
+ found = set()
+ for searchpath in self.searchpath:
+ for dirpath, dirnames, filenames in os.walk(searchpath):
+ for filename in filenames:
+ template = os.path.join(dirpath, filename) \
+ [len(searchpath):].strip(os.path.sep) \
+ .replace(os.path.sep, '/')
+ if template[:2] == './':
+ template = template[2:]
+ if template not in found:
+ found.add(template)
+ return sorted(found)
+
+
+class PackageLoader(BaseLoader):
+ """Load templates from python eggs or packages. It is constructed with
+ the name of the python package and the path to the templates in that
+ package::
+
+ loader = PackageLoader('mypackage', 'views')
+
+ If the package path is not given, ``'templates'`` is assumed.
+
+ Per default the template encoding is ``'utf-8'`` which can be changed
+ by setting the `encoding` parameter to something else. Due to the nature
+ of eggs it's only possible to reload templates if the package was loaded
+ from the file system and not a zip file.
+ """
+
+ def __init__(self, package_name, package_path='templates',
+ encoding='utf-8'):
+ from pkg_resources import DefaultProvider, ResourceManager, \
+ get_provider
+ provider = get_provider(package_name)
+ self.encoding = encoding
+ self.manager = ResourceManager()
+ self.filesystem_bound = isinstance(provider, DefaultProvider)
+ self.provider = provider
+ self.package_path = package_path
+
+ def get_source(self, environment, template):
+ pieces = split_template_path(template)
+ p = '/'.join((self.package_path,) + tuple(pieces))
+ if not self.provider.has_resource(p):
+ raise TemplateNotFound(template)
+
+ filename = uptodate = None
+ if self.filesystem_bound:
+ filename = self.provider.get_resource_filename(self.manager, p)
+ mtime = path.getmtime(filename)
+ def uptodate():
+ try:
+ return path.getmtime(filename) == mtime
+ except OSError:
+ return False
+
+ source = self.provider.get_resource_string(self.manager, p)
+ return source.decode(self.encoding), filename, uptodate
+
+ def list_templates(self):
+ path = self.package_path
+ if path[:2] == './':
+ path = path[2:]
+ elif path == '.':
+ path = ''
+ offset = len(path)
+ results = []
+ def _walk(path):
+ for filename in self.provider.resource_listdir(path):
+ fullname = path + '/' + filename
+ if self.provider.resource_isdir(fullname):
+ _walk(fullname)
+ else:
+ results.append(fullname[offset:].lstrip('/'))
+ _walk(path)
+ results.sort()
+ return results
+
+
+class DictLoader(BaseLoader):
+ """Loads a template from a python dict. It's passed a dict of unicode
+ strings bound to template names. This loader is useful for unittesting:
+
+ >>> loader = DictLoader({'index.html': 'source here'})
+
+ Because auto reloading is rarely useful this is disabled per default.
+ """
+
+ def __init__(self, mapping):
+ self.mapping = mapping
+
+ def get_source(self, environment, template):
+ if template in self.mapping:
+ source = self.mapping[template]
+ return source, None, lambda: source != self.mapping.get(template)
+ raise TemplateNotFound(template)
+
+ def list_templates(self):
+ return sorted(self.mapping)
+
+
+class FunctionLoader(BaseLoader):
+ """A loader that is passed a function which does the loading. The
+ function becomes the name of the template passed and has to return either
+ an unicode string with the template source, a tuple in the form ``(source,
+ filename, uptodatefunc)`` or `None` if the template does not exist.
+
+ >>> def load_template(name):
+ ... if name == 'index.html':
+ ... return '...'
+ ...
+ >>> loader = FunctionLoader(load_template)
+
+ The `uptodatefunc` is a function that is called if autoreload is enabled
+ and has to return `True` if the template is still up to date. For more
+ details have a look at :meth:`BaseLoader.get_source` which has the same
+ return value.
+ """
+
+ def __init__(self, load_func):
+ self.load_func = load_func
+
+ def get_source(self, environment, template):
+ rv = self.load_func(template)
+ if rv is None:
+ raise TemplateNotFound(template)
+ elif isinstance(rv, basestring):
+ return rv, None, None
+ return rv
+
+
+class PrefixLoader(BaseLoader):
+ """A loader that is passed a dict of loaders where each loader is bound
+ to a prefix. The prefix is delimited from the template by a slash per
+ default, which can be changed by setting the `delimiter` argument to
+ something else::
+
+ loader = PrefixLoader({
+ 'app1': PackageLoader('mypackage.app1'),
+ 'app2': PackageLoader('mypackage.app2')
+ })
+
+ By loading ``'app1/index.html'`` the file from the app1 package is loaded,
+ by loading ``'app2/index.html'`` the file from the second.
+ """
+
+ def __init__(self, mapping, delimiter='/'):
+ self.mapping = mapping
+ self.delimiter = delimiter
+
+ def get_loader(self, template):
+ try:
+ prefix, name = template.split(self.delimiter, 1)
+ loader = self.mapping[prefix]
+ except (ValueError, KeyError):
+ raise TemplateNotFound(template)
+ return loader, name
+
+ def get_source(self, environment, template):
+ loader, name = self.get_loader(template)
+ try:
+ return loader.get_source(environment, name)
+ except TemplateNotFound:
+ # re-raise the exception with the correct fileame here.
+ # (the one that includes the prefix)
+ raise TemplateNotFound(template)
+
+ @internalcode
+ def load(self, environment, name, globals=None):
+ loader, local_name = self.get_loader(name)
+ try:
+ return loader.load(environment, local_name)
+ except TemplateNotFound:
+ # re-raise the exception with the correct fileame here.
+ # (the one that includes the prefix)
+ raise TemplateNotFound(name)
+
+ def list_templates(self):
+ result = []
+ for prefix, loader in self.mapping.iteritems():
+ for template in loader.list_templates():
+ result.append(prefix + self.delimiter + template)
+ return result
+
+
+class ChoiceLoader(BaseLoader):
+ """This loader works like the `PrefixLoader` just that no prefix is
+ specified. If a template could not be found by one loader the next one
+ is tried.
+
+ >>> loader = ChoiceLoader([
+ ... FileSystemLoader('/path/to/user/templates'),
+ ... FileSystemLoader('/path/to/system/templates')
+ ... ])
+
+ This is useful if you want to allow users to override builtin templates
+ from a different location.
+ """
+
+ def __init__(self, loaders):
+ self.loaders = loaders
+
+ def get_source(self, environment, template):
+ for loader in self.loaders:
+ try:
+ return loader.get_source(environment, template)
+ except TemplateNotFound:
+ pass
+ raise TemplateNotFound(template)
+
+ @internalcode
+ def load(self, environment, name, globals=None):
+ for loader in self.loaders:
+ try:
+ return loader.load(environment, name, globals)
+ except TemplateNotFound:
+ pass
+ raise TemplateNotFound(name)
+
+ def list_templates(self):
+ found = set()
+ for loader in self.loaders:
+ found.update(loader.list_templates())
+ return sorted(found)
+
+
+class _TemplateModule(ModuleType):
+ """Like a normal module but with support for weak references"""
+
+
+class ModuleLoader(BaseLoader):
+ """This loader loads templates from precompiled templates.
+
+ Example usage:
+
+ >>> loader = ChoiceLoader([
+ ... ModuleLoader('/path/to/compiled/templates'),
+ ... FileSystemLoader('/path/to/templates')
+ ... ])
+
+ Templates can be precompiled with :meth:`Environment.compile_templates`.
+ """
+
+ has_source_access = False
+
+ def __init__(self, path):
+ package_name = '_jinja2_module_templates_%x' % id(self)
+
+ # create a fake module that looks for the templates in the
+ # path given.
+ mod = _TemplateModule(package_name)
+ if isinstance(path, basestring):
+ path = [path]
+ else:
+ path = list(path)
+ mod.__path__ = path
+
+ sys.modules[package_name] = weakref.proxy(mod,
+ lambda x: sys.modules.pop(package_name, None))
+
+ # the only strong reference, the sys.modules entry is weak
+ # so that the garbage collector can remove it once the
+ # loader that created it goes out of business.
+ self.module = mod
+ self.package_name = package_name
+
+ @staticmethod
+ def get_template_key(name):
+ return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest()
+
+ @staticmethod
+ def get_module_filename(name):
+ return ModuleLoader.get_template_key(name) + '.py'
+
+ @internalcode
+ def load(self, environment, name, globals=None):
+ key = self.get_template_key(name)
+ module = '%s.%s' % (self.package_name, key)
+ mod = getattr(self.module, module, None)
+ if mod is None:
+ try:
+ mod = __import__(module, None, None, ['root'])
+ except ImportError:
+ raise TemplateNotFound(name)
+
+ # remove the entry from sys.modules, we only want the attribute
+ # on the module object we have stored on the loader.
+ sys.modules.pop(module, None)
+
+ return environment.template_class.from_module_dict(
+ environment, mod.__dict__, globals)
diff --git a/websdk/jinja2/meta.py b/websdk/jinja2/meta.py
new file mode 100644
index 0000000..3a779a5
--- /dev/null
+++ b/websdk/jinja2/meta.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.meta
+ ~~~~~~~~~~~
+
+ This module implements various functions that exposes information about
+ templates that might be interesting for various kinds of applications.
+
+ :copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+from jinja2 import nodes
+from jinja2.compiler import CodeGenerator
+
+
+class TrackingCodeGenerator(CodeGenerator):
+ """We abuse the code generator for introspection."""
+
+ def __init__(self, environment):
+ CodeGenerator.__init__(self, environment, '<introspection>',
+ '<introspection>')
+ self.undeclared_identifiers = set()
+
+ def write(self, x):
+ """Don't write."""
+
+ def pull_locals(self, frame):
+ """Remember all undeclared identifiers."""
+ self.undeclared_identifiers.update(frame.identifiers.undeclared)
+
+
+def find_undeclared_variables(ast):
+ """Returns a set of all variables in the AST that will be looked up from
+ the context at runtime. Because at compile time it's not known which
+ variables will be used depending on the path the execution takes at
+ runtime, all variables are returned.
+
+ >>> from jinja2 import Environment, meta
+ >>> env = Environment()
+ >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
+ >>> meta.find_undeclared_variables(ast)
+ set(['bar'])
+
+ .. admonition:: Implementation
+
+ Internally the code generator is used for finding undeclared variables.
+ This is good to know because the code generator might raise a
+ :exc:`TemplateAssertionError` during compilation and as a matter of
+ fact this function can currently raise that exception as well.
+ """
+ codegen = TrackingCodeGenerator(ast.environment)
+ codegen.visit(ast)
+ return codegen.undeclared_identifiers
+
+
+def find_referenced_templates(ast):
+ """Finds all the referenced templates from the AST. This will return an
+ iterator over all the hardcoded template extensions, inclusions and
+ imports. If dynamic inheritance or inclusion is used, `None` will be
+ yielded.
+
+ >>> from jinja2 import Environment, meta
+ >>> env = Environment()
+ >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}')
+ >>> list(meta.find_referenced_templates(ast))
+ ['layout.html', None]
+
+ This function is useful for dependency tracking. For example if you want
+ to rebuild parts of the website after a layout template has changed.
+ """
+ for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import,
+ nodes.Include)):
+ if not isinstance(node.template, nodes.Const):
+ # a tuple with some non consts in there
+ if isinstance(node.template, (nodes.Tuple, nodes.List)):
+ for template_name in node.template.items:
+ # something const, only yield the strings and ignore
+ # non-string consts that really just make no sense
+ if isinstance(template_name, nodes.Const):
+ if isinstance(template_name.value, basestring):
+ yield template_name.value
+ # something dynamic in there
+ else:
+ yield None
+ # something dynamic we don't know about here
+ else:
+ yield None
+ continue
+ # constant is a basestring, direct template name
+ if isinstance(node.template.value, basestring):
+ yield node.template.value
+ # a tuple or list (latter *should* not happen) made of consts,
+ # yield the consts that are strings. We could warn here for
+ # non string values
+ elif isinstance(node, nodes.Include) and \
+ isinstance(node.template.value, (tuple, list)):
+ for template_name in node.template.value:
+ if isinstance(template_name, basestring):
+ yield template_name
+ # something else we don't care about, we could warn here
+ else:
+ yield None
diff --git a/websdk/jinja2/nodes.py b/websdk/jinja2/nodes.py
new file mode 100644
index 0000000..f9da1da
--- /dev/null
+++ b/websdk/jinja2/nodes.py
@@ -0,0 +1,910 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.nodes
+ ~~~~~~~~~~~~
+
+ This module implements additional nodes derived from the ast base node.
+
+ It also provides some node tree helper functions like `in_lineno` and
+ `get_nodes` used by the parser and translator in order to normalize
+ python and jinja nodes.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import operator
+from itertools import chain, izip
+from collections import deque
+from jinja2.utils import Markup, MethodType, FunctionType
+
+
+#: the types we support for context functions
+_context_function_types = (FunctionType, MethodType)
+
+
+_binop_to_func = {
+ '*': operator.mul,
+ '/': operator.truediv,
+ '//': operator.floordiv,
+ '**': operator.pow,
+ '%': operator.mod,
+ '+': operator.add,
+ '-': operator.sub
+}
+
+_uaop_to_func = {
+ 'not': operator.not_,
+ '+': operator.pos,
+ '-': operator.neg
+}
+
+_cmpop_to_func = {
+ 'eq': operator.eq,
+ 'ne': operator.ne,
+ 'gt': operator.gt,
+ 'gteq': operator.ge,
+ 'lt': operator.lt,
+ 'lteq': operator.le,
+ 'in': lambda a, b: a in b,
+ 'notin': lambda a, b: a not in b
+}
+
+
+class Impossible(Exception):
+ """Raised if the node could not perform a requested action."""
+
+
+class NodeType(type):
+ """A metaclass for nodes that handles the field and attribute
+ inheritance. fields and attributes from the parent class are
+ automatically forwarded to the child."""
+
+ def __new__(cls, name, bases, d):
+ for attr in 'fields', 'attributes':
+ storage = []
+ storage.extend(getattr(bases[0], attr, ()))
+ storage.extend(d.get(attr, ()))
+ assert len(bases) == 1, 'multiple inheritance not allowed'
+ assert len(storage) == len(set(storage)), 'layout conflict'
+ d[attr] = tuple(storage)
+ d.setdefault('abstract', False)
+ return type.__new__(cls, name, bases, d)
+
+
+class EvalContext(object):
+ """Holds evaluation time information. Custom attributes can be attached
+ to it in extensions.
+ """
+
+ def __init__(self, environment, template_name=None):
+ self.environment = environment
+ if callable(environment.autoescape):
+ self.autoescape = environment.autoescape(template_name)
+ else:
+ self.autoescape = environment.autoescape
+ self.volatile = False
+
+ def save(self):
+ return self.__dict__.copy()
+
+ def revert(self, old):
+ self.__dict__.clear()
+ self.__dict__.update(old)
+
+
+def get_eval_context(node, ctx):
+ if ctx is None:
+ if node.environment is None:
+ raise RuntimeError('if no eval context is passed, the '
+ 'node must have an attached '
+ 'environment.')
+ return EvalContext(node.environment)
+ return ctx
+
+
+class Node(object):
+ """Baseclass for all Jinja2 nodes. There are a number of nodes available
+ of different types. There are three major types:
+
+ - :class:`Stmt`: statements
+ - :class:`Expr`: expressions
+ - :class:`Helper`: helper nodes
+ - :class:`Template`: the outermost wrapper node
+
+ All nodes have fields and attributes. Fields may be other nodes, lists,
+ or arbitrary values. Fields are passed to the constructor as regular
+ positional arguments, attributes as keyword arguments. Each node has
+ two attributes: `lineno` (the line number of the node) and `environment`.
+ The `environment` attribute is set at the end of the parsing process for
+ all nodes automatically.
+ """
+ __metaclass__ = NodeType
+ fields = ()
+ attributes = ('lineno', 'environment')
+ abstract = True
+
+ def __init__(self, *fields, **attributes):
+ if self.abstract:
+ raise TypeError('abstract nodes are not instanciable')
+ if fields:
+ if len(fields) != len(self.fields):
+ if not self.fields:
+ raise TypeError('%r takes 0 arguments' %
+ self.__class__.__name__)
+ raise TypeError('%r takes 0 or %d argument%s' % (
+ self.__class__.__name__,
+ len(self.fields),
+ len(self.fields) != 1 and 's' or ''
+ ))
+ for name, arg in izip(self.fields, fields):
+ setattr(self, name, arg)
+ for attr in self.attributes:
+ setattr(self, attr, attributes.pop(attr, None))
+ if attributes:
+ raise TypeError('unknown attribute %r' %
+ iter(attributes).next())
+
+ def iter_fields(self, exclude=None, only=None):
+ """This method iterates over all fields that are defined and yields
+ ``(key, value)`` tuples. Per default all fields are returned, but
+ it's possible to limit that to some fields by providing the `only`
+ parameter or to exclude some using the `exclude` parameter. Both
+ should be sets or tuples of field names.
+ """
+ for name in self.fields:
+ if (exclude is only is None) or \
+ (exclude is not None and name not in exclude) or \
+ (only is not None and name in only):
+ try:
+ yield name, getattr(self, name)
+ except AttributeError:
+ pass
+
+ def iter_child_nodes(self, exclude=None, only=None):
+ """Iterates over all direct child nodes of the node. This iterates
+ over all fields and yields the values of they are nodes. If the value
+ of a field is a list all the nodes in that list are returned.
+ """
+ for field, item in self.iter_fields(exclude, only):
+ if isinstance(item, list):
+ for n in item:
+ if isinstance(n, Node):
+ yield n
+ elif isinstance(item, Node):
+ yield item
+
+ def find(self, node_type):
+ """Find the first node of a given type. If no such node exists the
+ return value is `None`.
+ """
+ for result in self.find_all(node_type):
+ return result
+
+ def find_all(self, node_type):
+ """Find all the nodes of a given type. If the type is a tuple,
+ the check is performed for any of the tuple items.
+ """
+ for child in self.iter_child_nodes():
+ if isinstance(child, node_type):
+ yield child
+ for result in child.find_all(node_type):
+ yield result
+
+ def set_ctx(self, ctx):
+ """Reset the context of a node and all child nodes. Per default the
+ parser will all generate nodes that have a 'load' context as it's the
+ most common one. This method is used in the parser to set assignment
+ targets and other nodes to a store context.
+ """
+ todo = deque([self])
+ while todo:
+ node = todo.popleft()
+ if 'ctx' in node.fields:
+ node.ctx = ctx
+ todo.extend(node.iter_child_nodes())
+ return self
+
+ def set_lineno(self, lineno, override=False):
+ """Set the line numbers of the node and children."""
+ todo = deque([self])
+ while todo:
+ node = todo.popleft()
+ if 'lineno' in node.attributes:
+ if node.lineno is None or override:
+ node.lineno = lineno
+ todo.extend(node.iter_child_nodes())
+ return self
+
+ def set_environment(self, environment):
+ """Set the environment for all nodes."""
+ todo = deque([self])
+ while todo:
+ node = todo.popleft()
+ node.environment = environment
+ todo.extend(node.iter_child_nodes())
+ return self
+
+ def __eq__(self, other):
+ return type(self) is type(other) and \
+ tuple(self.iter_fields()) == tuple(other.iter_fields())
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __repr__(self):
+ return '%s(%s)' % (
+ self.__class__.__name__,
+ ', '.join('%s=%r' % (arg, getattr(self, arg, None)) for
+ arg in self.fields)
+ )
+
+
+class Stmt(Node):
+ """Base node for all statements."""
+ abstract = True
+
+
+class Helper(Node):
+ """Nodes that exist in a specific context only."""
+ abstract = True
+
+
+class Template(Node):
+ """Node that represents a template. This must be the outermost node that
+ is passed to the compiler.
+ """
+ fields = ('body',)
+
+
+class Output(Stmt):
+ """A node that holds multiple expressions which are then printed out.
+ This is used both for the `print` statement and the regular template data.
+ """
+ fields = ('nodes',)
+
+
+class Extends(Stmt):
+ """Represents an extends statement."""
+ fields = ('template',)
+
+
+class For(Stmt):
+ """The for loop. `target` is the target for the iteration (usually a
+ :class:`Name` or :class:`Tuple`), `iter` the iterable. `body` is a list
+ of nodes that are used as loop-body, and `else_` a list of nodes for the
+ `else` block. If no else node exists it has to be an empty list.
+
+ For filtered nodes an expression can be stored as `test`, otherwise `None`.
+ """
+ fields = ('target', 'iter', 'body', 'else_', 'test', 'recursive')
+
+
+class If(Stmt):
+ """If `test` is true, `body` is rendered, else `else_`."""
+ fields = ('test', 'body', 'else_')
+
+
+class Macro(Stmt):
+ """A macro definition. `name` is the name of the macro, `args` a list of
+ arguments and `defaults` a list of defaults if there are any. `body` is
+ a list of nodes for the macro body.
+ """
+ fields = ('name', 'args', 'defaults', 'body')
+
+
+class CallBlock(Stmt):
+ """Like a macro without a name but a call instead. `call` is called with
+ the unnamed macro as `caller` argument this node holds.
+ """
+ fields = ('call', 'args', 'defaults', 'body')
+
+
+class FilterBlock(Stmt):
+ """Node for filter sections."""
+ fields = ('body', 'filter')
+
+
+class Block(Stmt):
+ """A node that represents a block."""
+ fields = ('name', 'body', 'scoped')
+
+
+class Include(Stmt):
+ """A node that represents the include tag."""
+ fields = ('template', 'with_context', 'ignore_missing')
+
+
+class Import(Stmt):
+ """A node that represents the import tag."""
+ fields = ('template', 'target', 'with_context')
+
+
+class FromImport(Stmt):
+ """A node that represents the from import tag. It's important to not
+ pass unsafe names to the name attribute. The compiler translates the
+ attribute lookups directly into getattr calls and does *not* use the
+ subscript callback of the interface. As exported variables may not
+ start with double underscores (which the parser asserts) this is not a
+ problem for regular Jinja code, but if this node is used in an extension
+ extra care must be taken.
+
+ The list of names may contain tuples if aliases are wanted.
+ """
+ fields = ('template', 'names', 'with_context')
+
+
+class ExprStmt(Stmt):
+ """A statement that evaluates an expression and discards the result."""
+ fields = ('node',)
+
+
+class Assign(Stmt):
+ """Assigns an expression to a target."""
+ fields = ('target', 'node')
+
+
+class Expr(Node):
+ """Baseclass for all expressions."""
+ abstract = True
+
+ def as_const(self, eval_ctx=None):
+ """Return the value of the expression as constant or raise
+ :exc:`Impossible` if this was not possible.
+
+ An :class:`EvalContext` can be provided, if none is given
+ a default context is created which requires the nodes to have
+ an attached environment.
+
+ .. versionchanged:: 2.4
+ the `eval_ctx` parameter was added.
+ """
+ raise Impossible()
+
+ def can_assign(self):
+ """Check if it's possible to assign something to this node."""
+ return False
+
+
+class BinExpr(Expr):
+ """Baseclass for all binary expressions."""
+ fields = ('left', 'right')
+ operator = None
+ abstract = True
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ # intercepted operators cannot be folded at compile time
+ if self.environment.sandboxed and \
+ self.operator in self.environment.intercepted_binops:
+ raise Impossible()
+ f = _binop_to_func[self.operator]
+ try:
+ return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx))
+ except Exception:
+ raise Impossible()
+
+
+class UnaryExpr(Expr):
+ """Baseclass for all unary expressions."""
+ fields = ('node',)
+ operator = None
+ abstract = True
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ # intercepted operators cannot be folded at compile time
+ if self.environment.sandboxed and \
+ self.operator in self.environment.intercepted_unops:
+ raise Impossible()
+ f = _uaop_to_func[self.operator]
+ try:
+ return f(self.node.as_const(eval_ctx))
+ except Exception:
+ raise Impossible()
+
+
+class Name(Expr):
+ """Looks up a name or stores a value in a name.
+ The `ctx` of the node can be one of the following values:
+
+ - `store`: store a value in the name
+ - `load`: load that name
+ - `param`: like `store` but if the name was defined as function parameter.
+ """
+ fields = ('name', 'ctx')
+
+ def can_assign(self):
+ return self.name not in ('true', 'false', 'none',
+ 'True', 'False', 'None')
+
+
+class Literal(Expr):
+ """Baseclass for literals."""
+ abstract = True
+
+
+class Const(Literal):
+ """All constant values. The parser will return this node for simple
+ constants such as ``42`` or ``"foo"`` but it can be used to store more
+ complex values such as lists too. Only constants with a safe
+ representation (objects where ``eval(repr(x)) == x`` is true).
+ """
+ fields = ('value',)
+
+ def as_const(self, eval_ctx=None):
+ return self.value
+
+ @classmethod
+ def from_untrusted(cls, value, lineno=None, environment=None):
+ """Return a const object if the value is representable as
+ constant value in the generated code, otherwise it will raise
+ an `Impossible` exception.
+ """
+ from compiler import has_safe_repr
+ if not has_safe_repr(value):
+ raise Impossible()
+ return cls(value, lineno=lineno, environment=environment)
+
+
+class TemplateData(Literal):
+ """A constant template string."""
+ fields = ('data',)
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ if eval_ctx.volatile:
+ raise Impossible()
+ if eval_ctx.autoescape:
+ return Markup(self.data)
+ return self.data
+
+
+class Tuple(Literal):
+ """For loop unpacking and some other things like multiple arguments
+ for subscripts. Like for :class:`Name` `ctx` specifies if the tuple
+ is used for loading the names or storing.
+ """
+ fields = ('items', 'ctx')
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ return tuple(x.as_const(eval_ctx) for x in self.items)
+
+ def can_assign(self):
+ for item in self.items:
+ if not item.can_assign():
+ return False
+ return True
+
+
+class List(Literal):
+ """Any list literal such as ``[1, 2, 3]``"""
+ fields = ('items',)
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ return [x.as_const(eval_ctx) for x in self.items]
+
+
+class Dict(Literal):
+ """Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of
+ :class:`Pair` nodes.
+ """
+ fields = ('items',)
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ return dict(x.as_const(eval_ctx) for x in self.items)
+
+
+class Pair(Helper):
+ """A key, value pair for dicts."""
+ fields = ('key', 'value')
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx)
+
+
+class Keyword(Helper):
+ """A key, value pair for keyword arguments where key is a string."""
+ fields = ('key', 'value')
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ return self.key, self.value.as_const(eval_ctx)
+
+
+class CondExpr(Expr):
+ """A conditional expression (inline if expression). (``{{
+ foo if bar else baz }}``)
+ """
+ fields = ('test', 'expr1', 'expr2')
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ if self.test.as_const(eval_ctx):
+ return self.expr1.as_const(eval_ctx)
+
+ # if we evaluate to an undefined object, we better do that at runtime
+ if self.expr2 is None:
+ raise Impossible()
+
+ return self.expr2.as_const(eval_ctx)
+
+
+class Filter(Expr):
+ """This node applies a filter on an expression. `name` is the name of
+ the filter, the rest of the fields are the same as for :class:`Call`.
+
+ If the `node` of a filter is `None` the contents of the last buffer are
+ filtered. Buffers are created by macros and filter blocks.
+ """
+ fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ if eval_ctx.volatile or self.node is None:
+ raise Impossible()
+ # we have to be careful here because we call filter_ below.
+ # if this variable would be called filter, 2to3 would wrap the
+ # call in a list beause it is assuming we are talking about the
+ # builtin filter function here which no longer returns a list in
+ # python 3. because of that, do not rename filter_ to filter!
+ filter_ = self.environment.filters.get(self.name)
+ if filter_ is None or getattr(filter_, 'contextfilter', False):
+ raise Impossible()
+ obj = self.node.as_const(eval_ctx)
+ args = [x.as_const(eval_ctx) for x in self.args]
+ if getattr(filter_, 'evalcontextfilter', False):
+ args.insert(0, eval_ctx)
+ elif getattr(filter_, 'environmentfilter', False):
+ args.insert(0, self.environment)
+ kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
+ if self.dyn_args is not None:
+ try:
+ args.extend(self.dyn_args.as_const(eval_ctx))
+ except Exception:
+ raise Impossible()
+ if self.dyn_kwargs is not None:
+ try:
+ kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
+ except Exception:
+ raise Impossible()
+ try:
+ return filter_(obj, *args, **kwargs)
+ except Exception:
+ raise Impossible()
+
+
+class Test(Expr):
+ """Applies a test on an expression. `name` is the name of the test, the
+ rest of the fields are the same as for :class:`Call`.
+ """
+ fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+
+
+class Call(Expr):
+ """Calls an expression. `args` is a list of arguments, `kwargs` a list
+ of keyword arguments (list of :class:`Keyword` nodes), and `dyn_args`
+ and `dyn_kwargs` has to be either `None` or a node that is used as
+ node for dynamic positional (``*args``) or keyword (``**kwargs``)
+ arguments.
+ """
+ fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ if eval_ctx.volatile:
+ raise Impossible()
+ obj = self.node.as_const(eval_ctx)
+
+ # don't evaluate context functions
+ args = [x.as_const(eval_ctx) for x in self.args]
+ if isinstance(obj, _context_function_types):
+ if getattr(obj, 'contextfunction', False):
+ raise Impossible()
+ elif getattr(obj, 'evalcontextfunction', False):
+ args.insert(0, eval_ctx)
+ elif getattr(obj, 'environmentfunction', False):
+ args.insert(0, self.environment)
+
+ kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
+ if self.dyn_args is not None:
+ try:
+ args.extend(self.dyn_args.as_const(eval_ctx))
+ except Exception:
+ raise Impossible()
+ if self.dyn_kwargs is not None:
+ try:
+ kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
+ except Exception:
+ raise Impossible()
+ try:
+ return obj(*args, **kwargs)
+ except Exception:
+ raise Impossible()
+
+
+class Getitem(Expr):
+ """Get an attribute or item from an expression and prefer the item."""
+ fields = ('node', 'arg', 'ctx')
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ if self.ctx != 'load':
+ raise Impossible()
+ try:
+ return self.environment.getitem(self.node.as_const(eval_ctx),
+ self.arg.as_const(eval_ctx))
+ except Exception:
+ raise Impossible()
+
+ def can_assign(self):
+ return False
+
+
+class Getattr(Expr):
+ """Get an attribute or item from an expression that is a ascii-only
+ bytestring and prefer the attribute.
+ """
+ fields = ('node', 'attr', 'ctx')
+
+ def as_const(self, eval_ctx=None):
+ if self.ctx != 'load':
+ raise Impossible()
+ try:
+ eval_ctx = get_eval_context(self, eval_ctx)
+ return self.environment.getattr(self.node.as_const(eval_ctx),
+ self.attr)
+ except Exception:
+ raise Impossible()
+
+ def can_assign(self):
+ return False
+
+
+class Slice(Expr):
+ """Represents a slice object. This must only be used as argument for
+ :class:`Subscript`.
+ """
+ fields = ('start', 'stop', 'step')
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ def const(obj):
+ if obj is None:
+ return None
+ return obj.as_const(eval_ctx)
+ return slice(const(self.start), const(self.stop), const(self.step))
+
+
+class Concat(Expr):
+ """Concatenates the list of expressions provided after converting them to
+ unicode.
+ """
+ fields = ('nodes',)
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ return ''.join(unicode(x.as_const(eval_ctx)) for x in self.nodes)
+
+
+class Compare(Expr):
+ """Compares an expression with some other expressions. `ops` must be a
+ list of :class:`Operand`\s.
+ """
+ fields = ('expr', 'ops')
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ result = value = self.expr.as_const(eval_ctx)
+ try:
+ for op in self.ops:
+ new_value = op.expr.as_const(eval_ctx)
+ result = _cmpop_to_func[op.op](value, new_value)
+ value = new_value
+ except Exception:
+ raise Impossible()
+ return result
+
+
+class Operand(Helper):
+ """Holds an operator and an expression."""
+ fields = ('op', 'expr')
+
+if __debug__:
+ Operand.__doc__ += '\nThe following operators are available: ' + \
+ ', '.join(sorted('``%s``' % x for x in set(_binop_to_func) |
+ set(_uaop_to_func) | set(_cmpop_to_func)))
+
+
+class Mul(BinExpr):
+ """Multiplies the left with the right node."""
+ operator = '*'
+
+
+class Div(BinExpr):
+ """Divides the left by the right node."""
+ operator = '/'
+
+
+class FloorDiv(BinExpr):
+ """Divides the left by the right node and truncates conver the
+ result into an integer by truncating.
+ """
+ operator = '//'
+
+
+class Add(BinExpr):
+ """Add the left to the right node."""
+ operator = '+'
+
+
+class Sub(BinExpr):
+ """Substract the right from the left node."""
+ operator = '-'
+
+
+class Mod(BinExpr):
+ """Left modulo right."""
+ operator = '%'
+
+
+class Pow(BinExpr):
+ """Left to the power of right."""
+ operator = '**'
+
+
+class And(BinExpr):
+ """Short circuited AND."""
+ operator = 'and'
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx)
+
+
+class Or(BinExpr):
+ """Short circuited OR."""
+ operator = 'or'
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx)
+
+
+class Not(UnaryExpr):
+ """Negate the expression."""
+ operator = 'not'
+
+
+class Neg(UnaryExpr):
+ """Make the expression negative."""
+ operator = '-'
+
+
+class Pos(UnaryExpr):
+ """Make the expression positive (noop for most expressions)"""
+ operator = '+'
+
+
+# Helpers for extensions
+
+
+class EnvironmentAttribute(Expr):
+ """Loads an attribute from the environment object. This is useful for
+ extensions that want to call a callback stored on the environment.
+ """
+ fields = ('name',)
+
+
+class ExtensionAttribute(Expr):
+ """Returns the attribute of an extension bound to the environment.
+ The identifier is the identifier of the :class:`Extension`.
+
+ This node is usually constructed by calling the
+ :meth:`~jinja2.ext.Extension.attr` method on an extension.
+ """
+ fields = ('identifier', 'name')
+
+
+class ImportedName(Expr):
+ """If created with an import name the import name is returned on node
+ access. For example ``ImportedName('cgi.escape')`` returns the `escape`
+ function from the cgi module on evaluation. Imports are optimized by the
+ compiler so there is no need to assign them to local variables.
+ """
+ fields = ('importname',)
+
+
+class InternalName(Expr):
+ """An internal name in the compiler. You cannot create these nodes
+ yourself but the parser provides a
+ :meth:`~jinja2.parser.Parser.free_identifier` method that creates
+ a new identifier for you. This identifier is not available from the
+ template and is not threated specially by the compiler.
+ """
+ fields = ('name',)
+
+ def __init__(self):
+ raise TypeError('Can\'t create internal names. Use the '
+ '`free_identifier` method on a parser.')
+
+
+class MarkSafe(Expr):
+ """Mark the wrapped expression as safe (wrap it as `Markup`)."""
+ fields = ('expr',)
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ return Markup(self.expr.as_const(eval_ctx))
+
+
+class MarkSafeIfAutoescape(Expr):
+ """Mark the wrapped expression as safe (wrap it as `Markup`) but
+ only if autoescaping is active.
+
+ .. versionadded:: 2.5
+ """
+ fields = ('expr',)
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ if eval_ctx.volatile:
+ raise Impossible()
+ expr = self.expr.as_const(eval_ctx)
+ if eval_ctx.autoescape:
+ return Markup(expr)
+ return expr
+
+
+class ContextReference(Expr):
+ """Returns the current template context. It can be used like a
+ :class:`Name` node, with a ``'load'`` ctx and will return the
+ current :class:`~jinja2.runtime.Context` object.
+
+ Here an example that assigns the current template name to a
+ variable named `foo`::
+
+ Assign(Name('foo', ctx='store'),
+ Getattr(ContextReference(), 'name'))
+ """
+
+
+class Continue(Stmt):
+ """Continue a loop."""
+
+
+class Break(Stmt):
+ """Break a loop."""
+
+
+class Scope(Stmt):
+ """An artificial scope."""
+ fields = ('body',)
+
+
+class EvalContextModifier(Stmt):
+ """Modifies the eval context. For each option that should be modified,
+ a :class:`Keyword` has to be added to the :attr:`options` list.
+
+ Example to change the `autoescape` setting::
+
+ EvalContextModifier(options=[Keyword('autoescape', Const(True))])
+ """
+ fields = ('options',)
+
+
+class ScopedEvalContextModifier(EvalContextModifier):
+ """Modifies the eval context and reverts it later. Works exactly like
+ :class:`EvalContextModifier` but will only modify the
+ :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`.
+ """
+ fields = ('body',)
+
+
+# make sure nobody creates custom nodes
+def _failing_new(*args, **kwargs):
+ raise TypeError('can\'t create custom node types')
+NodeType.__new__ = staticmethod(_failing_new); del _failing_new
diff --git a/websdk/jinja2/optimizer.py b/websdk/jinja2/optimizer.py
new file mode 100644
index 0000000..00eab11
--- /dev/null
+++ b/websdk/jinja2/optimizer.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.optimizer
+ ~~~~~~~~~~~~~~~~
+
+ The jinja optimizer is currently trying to constant fold a few expressions
+ and modify the AST in place so that it should be easier to evaluate it.
+
+ Because the AST does not contain all the scoping information and the
+ compiler has to find that out, we cannot do all the optimizations we
+ want. For example loop unrolling doesn't work because unrolled loops would
+ have a different scoping.
+
+ The solution would be a second syntax tree that has the scoping rules stored.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD.
+"""
+from jinja2 import nodes
+from jinja2.visitor import NodeTransformer
+
+
+def optimize(node, environment):
+ """The context hint can be used to perform an static optimization
+ based on the context given."""
+ optimizer = Optimizer(environment)
+ return optimizer.visit(node)
+
+
+class Optimizer(NodeTransformer):
+
+ def __init__(self, environment):
+ self.environment = environment
+
+ def visit_If(self, node):
+ """Eliminate dead code."""
+ # do not optimize ifs that have a block inside so that it doesn't
+ # break super().
+ if node.find(nodes.Block) is not None:
+ return self.generic_visit(node)
+ try:
+ val = self.visit(node.test).as_const()
+ except nodes.Impossible:
+ return self.generic_visit(node)
+ if val:
+ body = node.body
+ else:
+ body = node.else_
+ result = []
+ for node in body:
+ result.extend(self.visit_list(node))
+ return result
+
+ def fold(self, node):
+ """Do constant folding."""
+ node = self.generic_visit(node)
+ try:
+ return nodes.Const.from_untrusted(node.as_const(),
+ lineno=node.lineno,
+ environment=self.environment)
+ except nodes.Impossible:
+ return node
+
+ visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \
+ visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \
+ visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \
+ visit_Filter = visit_Test = visit_CondExpr = fold
+ del fold
diff --git a/websdk/jinja2/parser.py b/websdk/jinja2/parser.py
new file mode 100644
index 0000000..2125338
--- /dev/null
+++ b/websdk/jinja2/parser.py
@@ -0,0 +1,895 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.parser
+ ~~~~~~~~~~~~~
+
+ Implements the template parser.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+from jinja2 import nodes
+from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
+from jinja2.utils import next
+from jinja2.lexer import describe_token, describe_token_expr
+
+
+#: statements that callinto
+_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
+ 'macro', 'include', 'from', 'import',
+ 'set'])
+_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
+
+
+class Parser(object):
+ """This is the central parsing class Jinja2 uses. It's passed to
+ extensions and can be used to parse expressions or statements.
+ """
+
+ def __init__(self, environment, source, name=None, filename=None,
+ state=None):
+ self.environment = environment
+ self.stream = environment._tokenize(source, name, filename, state)
+ self.name = name
+ self.filename = filename
+ self.closed = False
+ self.extensions = {}
+ for extension in environment.iter_extensions():
+ for tag in extension.tags:
+ self.extensions[tag] = extension.parse
+ self._last_identifier = 0
+ self._tag_stack = []
+ self._end_token_stack = []
+
+ def fail(self, msg, lineno=None, exc=TemplateSyntaxError):
+ """Convenience method that raises `exc` with the message, passed
+ line number or last line number as well as the current name and
+ filename.
+ """
+ if lineno is None:
+ lineno = self.stream.current.lineno
+ raise exc(msg, lineno, self.name, self.filename)
+
+ def _fail_ut_eof(self, name, end_token_stack, lineno):
+ expected = []
+ for exprs in end_token_stack:
+ expected.extend(map(describe_token_expr, exprs))
+ if end_token_stack:
+ currently_looking = ' or '.join(
+ "'%s'" % describe_token_expr(expr)
+ for expr in end_token_stack[-1])
+ else:
+ currently_looking = None
+
+ if name is None:
+ message = ['Unexpected end of template.']
+ else:
+ message = ['Encountered unknown tag \'%s\'.' % name]
+
+ if currently_looking:
+ if name is not None and name in expected:
+ message.append('You probably made a nesting mistake. Jinja '
+ 'is expecting this tag, but currently looking '
+ 'for %s.' % currently_looking)
+ else:
+ message.append('Jinja was looking for the following tags: '
+ '%s.' % currently_looking)
+
+ if self._tag_stack:
+ message.append('The innermost block that needs to be '
+ 'closed is \'%s\'.' % self._tag_stack[-1])
+
+ self.fail(' '.join(message), lineno)
+
+ def fail_unknown_tag(self, name, lineno=None):
+ """Called if the parser encounters an unknown tag. Tries to fail
+ with a human readable error message that could help to identify
+ the problem.
+ """
+ return self._fail_ut_eof(name, self._end_token_stack, lineno)
+
+ def fail_eof(self, end_tokens=None, lineno=None):
+ """Like fail_unknown_tag but for end of template situations."""
+ stack = list(self._end_token_stack)
+ if end_tokens is not None:
+ stack.append(end_tokens)
+ return self._fail_ut_eof(None, stack, lineno)
+
+ def is_tuple_end(self, extra_end_rules=None):
+ """Are we at the end of a tuple?"""
+ if self.stream.current.type in ('variable_end', 'block_end', 'rparen'):
+ return True
+ elif extra_end_rules is not None:
+ return self.stream.current.test_any(extra_end_rules)
+ return False
+
+ def free_identifier(self, lineno=None):
+ """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
+ self._last_identifier += 1
+ rv = object.__new__(nodes.InternalName)
+ nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
+ return rv
+
+ def parse_statement(self):
+ """Parse a single statement."""
+ token = self.stream.current
+ if token.type != 'name':
+ self.fail('tag name expected', token.lineno)
+ self._tag_stack.append(token.value)
+ pop_tag = True
+ try:
+ if token.value in _statement_keywords:
+ return getattr(self, 'parse_' + self.stream.current.value)()
+ if token.value == 'call':
+ return self.parse_call_block()
+ if token.value == 'filter':
+ return self.parse_filter_block()
+ ext = self.extensions.get(token.value)
+ if ext is not None:
+ return ext(self)
+
+ # did not work out, remove the token we pushed by accident
+ # from the stack so that the unknown tag fail function can
+ # produce a proper error message.
+ self._tag_stack.pop()
+ pop_tag = False
+ self.fail_unknown_tag(token.value, token.lineno)
+ finally:
+ if pop_tag:
+ self._tag_stack.pop()
+
+ def parse_statements(self, end_tokens, drop_needle=False):
+ """Parse multiple statements into a list until one of the end tokens
+ is reached. This is used to parse the body of statements as it also
+ parses template data if appropriate. The parser checks first if the
+ current token is a colon and skips it if there is one. Then it checks
+ for the block end and parses until if one of the `end_tokens` is
+ reached. Per default the active token in the stream at the end of
+ the call is the matched end token. If this is not wanted `drop_needle`
+ can be set to `True` and the end token is removed.
+ """
+ # the first token may be a colon for python compatibility
+ self.stream.skip_if('colon')
+
+ # in the future it would be possible to add whole code sections
+ # by adding some sort of end of statement token and parsing those here.
+ self.stream.expect('block_end')
+ result = self.subparse(end_tokens)
+
+ # we reached the end of the template too early, the subparser
+ # does not check for this, so we do that now
+ if self.stream.current.type == 'eof':
+ self.fail_eof(end_tokens)
+
+ if drop_needle:
+ next(self.stream)
+ return result
+
+ def parse_set(self):
+ """Parse an assign statement."""
+ lineno = next(self.stream).lineno
+ target = self.parse_assign_target()
+ self.stream.expect('assign')
+ expr = self.parse_tuple()
+ return nodes.Assign(target, expr, lineno=lineno)
+
+ def parse_for(self):
+ """Parse a for loop."""
+ lineno = self.stream.expect('name:for').lineno
+ target = self.parse_assign_target(extra_end_rules=('name:in',))
+ self.stream.expect('name:in')
+ iter = self.parse_tuple(with_condexpr=False,
+ extra_end_rules=('name:recursive',))
+ test = None
+ if self.stream.skip_if('name:if'):
+ test = self.parse_expression()
+ recursive = self.stream.skip_if('name:recursive')
+ body = self.parse_statements(('name:endfor', 'name:else'))
+ if next(self.stream).value == 'endfor':
+ else_ = []
+ else:
+ else_ = self.parse_statements(('name:endfor',), drop_needle=True)
+ return nodes.For(target, iter, body, else_, test,
+ recursive, lineno=lineno)
+
+ def parse_if(self):
+ """Parse an if construct."""
+ node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
+ while 1:
+ node.test = self.parse_tuple(with_condexpr=False)
+ node.body = self.parse_statements(('name:elif', 'name:else',
+ 'name:endif'))
+ token = next(self.stream)
+ if token.test('name:elif'):
+ new_node = nodes.If(lineno=self.stream.current.lineno)
+ node.else_ = [new_node]
+ node = new_node
+ continue
+ elif token.test('name:else'):
+ node.else_ = self.parse_statements(('name:endif',),
+ drop_needle=True)
+ else:
+ node.else_ = []
+ break
+ return result
+
+ def parse_block(self):
+ node = nodes.Block(lineno=next(self.stream).lineno)
+ node.name = self.stream.expect('name').value
+ node.scoped = self.stream.skip_if('name:scoped')
+
+ # common problem people encounter when switching from django
+ # to jinja. we do not support hyphens in block names, so let's
+ # raise a nicer error message in that case.
+ if self.stream.current.type == 'sub':
+ self.fail('Block names in Jinja have to be valid Python '
+ 'identifiers and may not contain hyphens, use an '
+ 'underscore instead.')
+
+ node.body = self.parse_statements(('name:endblock',), drop_needle=True)
+ self.stream.skip_if('name:' + node.name)
+ return node
+
+ def parse_extends(self):
+ node = nodes.Extends(lineno=next(self.stream).lineno)
+ node.template = self.parse_expression()
+ return node
+
+ def parse_import_context(self, node, default):
+ if self.stream.current.test_any('name:with', 'name:without') and \
+ self.stream.look().test('name:context'):
+ node.with_context = next(self.stream).value == 'with'
+ self.stream.skip()
+ else:
+ node.with_context = default
+ return node
+
+ def parse_include(self):
+ node = nodes.Include(lineno=next(self.stream).lineno)
+ node.template = self.parse_expression()
+ if self.stream.current.test('name:ignore') and \
+ self.stream.look().test('name:missing'):
+ node.ignore_missing = True
+ self.stream.skip(2)
+ else:
+ node.ignore_missing = False
+ return self.parse_import_context(node, True)
+
+ def parse_import(self):
+ node = nodes.Import(lineno=next(self.stream).lineno)
+ node.template = self.parse_expression()
+ self.stream.expect('name:as')
+ node.target = self.parse_assign_target(name_only=True).name
+ return self.parse_import_context(node, False)
+
+ def parse_from(self):
+ node = nodes.FromImport(lineno=next(self.stream).lineno)
+ node.template = self.parse_expression()
+ self.stream.expect('name:import')
+ node.names = []
+
+ def parse_context():
+ if self.stream.current.value in ('with', 'without') and \
+ self.stream.look().test('name:context'):
+ node.with_context = next(self.stream).value == 'with'
+ self.stream.skip()
+ return True
+ return False
+
+ while 1:
+ if node.names:
+ self.stream.expect('comma')
+ if self.stream.current.type == 'name':
+ if parse_context():
+ break
+ target = self.parse_assign_target(name_only=True)
+ if target.name.startswith('_'):
+ self.fail('names starting with an underline can not '
+ 'be imported', target.lineno,
+ exc=TemplateAssertionError)
+ if self.stream.skip_if('name:as'):
+ alias = self.parse_assign_target(name_only=True)
+ node.names.append((target.name, alias.name))
+ else:
+ node.names.append(target.name)
+ if parse_context() or self.stream.current.type != 'comma':
+ break
+ else:
+ break
+ if not hasattr(node, 'with_context'):
+ node.with_context = False
+ self.stream.skip_if('comma')
+ return node
+
+ def parse_signature(self, node):
+ node.args = args = []
+ node.defaults = defaults = []
+ self.stream.expect('lparen')
+ while self.stream.current.type != 'rparen':
+ if args:
+ self.stream.expect('comma')
+ arg = self.parse_assign_target(name_only=True)
+ arg.set_ctx('param')
+ if self.stream.skip_if('assign'):
+ defaults.append(self.parse_expression())
+ args.append(arg)
+ self.stream.expect('rparen')
+
+ def parse_call_block(self):
+ node = nodes.CallBlock(lineno=next(self.stream).lineno)
+ if self.stream.current.type == 'lparen':
+ self.parse_signature(node)
+ else:
+ node.args = []
+ node.defaults = []
+
+ node.call = self.parse_expression()
+ if not isinstance(node.call, nodes.Call):
+ self.fail('expected call', node.lineno)
+ node.body = self.parse_statements(('name:endcall',), drop_needle=True)
+ return node
+
+ def parse_filter_block(self):
+ node = nodes.FilterBlock(lineno=next(self.stream).lineno)
+ node.filter = self.parse_filter(None, start_inline=True)
+ node.body = self.parse_statements(('name:endfilter',),
+ drop_needle=True)
+ return node
+
+ def parse_macro(self):
+ node = nodes.Macro(lineno=next(self.stream).lineno)
+ node.name = self.parse_assign_target(name_only=True).name
+ self.parse_signature(node)
+ node.body = self.parse_statements(('name:endmacro',),
+ drop_needle=True)
+ return node
+
+ def parse_print(self):
+ node = nodes.Output(lineno=next(self.stream).lineno)
+ node.nodes = []
+ while self.stream.current.type != 'block_end':
+ if node.nodes:
+ self.stream.expect('comma')
+ node.nodes.append(self.parse_expression())
+ return node
+
+ def parse_assign_target(self, with_tuple=True, name_only=False,
+ extra_end_rules=None):
+ """Parse an assignment target. As Jinja2 allows assignments to
+ tuples, this function can parse all allowed assignment targets. Per
+ default assignments to tuples are parsed, that can be disable however
+ by setting `with_tuple` to `False`. If only assignments to names are
+ wanted `name_only` can be set to `True`. The `extra_end_rules`
+ parameter is forwarded to the tuple parsing function.
+ """
+ if name_only:
+ token = self.stream.expect('name')
+ target = nodes.Name(token.value, 'store', lineno=token.lineno)
+ else:
+ if with_tuple:
+ target = self.parse_tuple(simplified=True,
+ extra_end_rules=extra_end_rules)
+ else:
+ target = self.parse_primary()
+ target.set_ctx('store')
+ if not target.can_assign():
+ self.fail('can\'t assign to %r' % target.__class__.
+ __name__.lower(), target.lineno)
+ return target
+
+ def parse_expression(self, with_condexpr=True):
+ """Parse an expression. Per default all expressions are parsed, if
+ the optional `with_condexpr` parameter is set to `False` conditional
+ expressions are not parsed.
+ """
+ if with_condexpr:
+ return self.parse_condexpr()
+ return self.parse_or()
+
+ def parse_condexpr(self):
+ lineno = self.stream.current.lineno
+ expr1 = self.parse_or()
+ while self.stream.skip_if('name:if'):
+ expr2 = self.parse_or()
+ if self.stream.skip_if('name:else'):
+ expr3 = self.parse_condexpr()
+ else:
+ expr3 = None
+ expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return expr1
+
+ def parse_or(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_and()
+ while self.stream.skip_if('name:or'):
+ right = self.parse_and()
+ left = nodes.Or(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_and(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_not()
+ while self.stream.skip_if('name:and'):
+ right = self.parse_not()
+ left = nodes.And(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_not(self):
+ if self.stream.current.test('name:not'):
+ lineno = next(self.stream).lineno
+ return nodes.Not(self.parse_not(), lineno=lineno)
+ return self.parse_compare()
+
+ def parse_compare(self):
+ lineno = self.stream.current.lineno
+ expr = self.parse_add()
+ ops = []
+ while 1:
+ token_type = self.stream.current.type
+ if token_type in _compare_operators:
+ next(self.stream)
+ ops.append(nodes.Operand(token_type, self.parse_add()))
+ elif self.stream.skip_if('name:in'):
+ ops.append(nodes.Operand('in', self.parse_add()))
+ elif self.stream.current.test('name:not') and \
+ self.stream.look().test('name:in'):
+ self.stream.skip(2)
+ ops.append(nodes.Operand('notin', self.parse_add()))
+ else:
+ break
+ lineno = self.stream.current.lineno
+ if not ops:
+ return expr
+ return nodes.Compare(expr, ops, lineno=lineno)
+
+ def parse_add(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_sub()
+ while self.stream.current.type == 'add':
+ next(self.stream)
+ right = self.parse_sub()
+ left = nodes.Add(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_sub(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_concat()
+ while self.stream.current.type == 'sub':
+ next(self.stream)
+ right = self.parse_concat()
+ left = nodes.Sub(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_concat(self):
+ lineno = self.stream.current.lineno
+ args = [self.parse_mul()]
+ while self.stream.current.type == 'tilde':
+ next(self.stream)
+ args.append(self.parse_mul())
+ if len(args) == 1:
+ return args[0]
+ return nodes.Concat(args, lineno=lineno)
+
+ def parse_mul(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_div()
+ while self.stream.current.type == 'mul':
+ next(self.stream)
+ right = self.parse_div()
+ left = nodes.Mul(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_div(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_floordiv()
+ while self.stream.current.type == 'div':
+ next(self.stream)
+ right = self.parse_floordiv()
+ left = nodes.Div(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_floordiv(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_mod()
+ while self.stream.current.type == 'floordiv':
+ next(self.stream)
+ right = self.parse_mod()
+ left = nodes.FloorDiv(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_mod(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_pow()
+ while self.stream.current.type == 'mod':
+ next(self.stream)
+ right = self.parse_pow()
+ left = nodes.Mod(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_pow(self):
+ lineno = self.stream.current.lineno
+ left = self.parse_unary()
+ while self.stream.current.type == 'pow':
+ next(self.stream)
+ right = self.parse_unary()
+ left = nodes.Pow(left, right, lineno=lineno)
+ lineno = self.stream.current.lineno
+ return left
+
+ def parse_unary(self, with_filter=True):
+ token_type = self.stream.current.type
+ lineno = self.stream.current.lineno
+ if token_type == 'sub':
+ next(self.stream)
+ node = nodes.Neg(self.parse_unary(False), lineno=lineno)
+ elif token_type == 'add':
+ next(self.stream)
+ node = nodes.Pos(self.parse_unary(False), lineno=lineno)
+ else:
+ node = self.parse_primary()
+ node = self.parse_postfix(node)
+ if with_filter:
+ node = self.parse_filter_expr(node)
+ return node
+
+ def parse_primary(self):
+ token = self.stream.current
+ if token.type == 'name':
+ if token.value in ('true', 'false', 'True', 'False'):
+ node = nodes.Const(token.value in ('true', 'True'),
+ lineno=token.lineno)
+ elif token.value in ('none', 'None'):
+ node = nodes.Const(None, lineno=token.lineno)
+ else:
+ node = nodes.Name(token.value, 'load', lineno=token.lineno)
+ next(self.stream)
+ elif token.type == 'string':
+ next(self.stream)
+ buf = [token.value]
+ lineno = token.lineno
+ while self.stream.current.type == 'string':
+ buf.append(self.stream.current.value)
+ next(self.stream)
+ node = nodes.Const(''.join(buf), lineno=lineno)
+ elif token.type in ('integer', 'float'):
+ next(self.stream)
+ node = nodes.Const(token.value, lineno=token.lineno)
+ elif token.type == 'lparen':
+ next(self.stream)
+ node = self.parse_tuple(explicit_parentheses=True)
+ self.stream.expect('rparen')
+ elif token.type == 'lbracket':
+ node = self.parse_list()
+ elif token.type == 'lbrace':
+ node = self.parse_dict()
+ else:
+ self.fail("unexpected '%s'" % describe_token(token), token.lineno)
+ return node
+
+ def parse_tuple(self, simplified=False, with_condexpr=True,
+ extra_end_rules=None, explicit_parentheses=False):
+ """Works like `parse_expression` but if multiple expressions are
+ delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
+ This method could also return a regular expression instead of a tuple
+ if no commas where found.
+
+ The default parsing mode is a full tuple. If `simplified` is `True`
+ only names and literals are parsed. The `no_condexpr` parameter is
+ forwarded to :meth:`parse_expression`.
+
+ Because tuples do not require delimiters and may end in a bogus comma
+ an extra hint is needed that marks the end of a tuple. For example
+ for loops support tuples between `for` and `in`. In that case the
+ `extra_end_rules` is set to ``['name:in']``.
+
+ `explicit_parentheses` is true if the parsing was triggered by an
+ expression in parentheses. This is used to figure out if an empty
+ tuple is a valid expression or not.
+ """
+ lineno = self.stream.current.lineno
+ if simplified:
+ parse = self.parse_primary
+ elif with_condexpr:
+ parse = self.parse_expression
+ else:
+ parse = lambda: self.parse_expression(with_condexpr=False)
+ args = []
+ is_tuple = False
+ while 1:
+ if args:
+ self.stream.expect('comma')
+ if self.is_tuple_end(extra_end_rules):
+ break
+ args.append(parse())
+ if self.stream.current.type == 'comma':
+ is_tuple = True
+ else:
+ break
+ lineno = self.stream.current.lineno
+
+ if not is_tuple:
+ if args:
+ return args[0]
+
+ # if we don't have explicit parentheses, an empty tuple is
+ # not a valid expression. This would mean nothing (literally
+ # nothing) in the spot of an expression would be an empty
+ # tuple.
+ if not explicit_parentheses:
+ self.fail('Expected an expression, got \'%s\'' %
+ describe_token(self.stream.current))
+
+ return nodes.Tuple(args, 'load', lineno=lineno)
+
+ def parse_list(self):
+ token = self.stream.expect('lbracket')
+ items = []
+ while self.stream.current.type != 'rbracket':
+ if items:
+ self.stream.expect('comma')
+ if self.stream.current.type == 'rbracket':
+ break
+ items.append(self.parse_expression())
+ self.stream.expect('rbracket')
+ return nodes.List(items, lineno=token.lineno)
+
+ def parse_dict(self):
+ token = self.stream.expect('lbrace')
+ items = []
+ while self.stream.current.type != 'rbrace':
+ if items:
+ self.stream.expect('comma')
+ if self.stream.current.type == 'rbrace':
+ break
+ key = self.parse_expression()
+ self.stream.expect('colon')
+ value = self.parse_expression()
+ items.append(nodes.Pair(key, value, lineno=key.lineno))
+ self.stream.expect('rbrace')
+ return nodes.Dict(items, lineno=token.lineno)
+
+ def parse_postfix(self, node):
+ while 1:
+ token_type = self.stream.current.type
+ if token_type == 'dot' or token_type == 'lbracket':
+ node = self.parse_subscript(node)
+ # calls are valid both after postfix expressions (getattr
+ # and getitem) as well as filters and tests
+ elif token_type == 'lparen':
+ node = self.parse_call(node)
+ else:
+ break
+ return node
+
+ def parse_filter_expr(self, node):
+ while 1:
+ token_type = self.stream.current.type
+ if token_type == 'pipe':
+ node = self.parse_filter(node)
+ elif token_type == 'name' and self.stream.current.value == 'is':
+ node = self.parse_test(node)
+ # calls are valid both after postfix expressions (getattr
+ # and getitem) as well as filters and tests
+ elif token_type == 'lparen':
+ node = self.parse_call(node)
+ else:
+ break
+ return node
+
+ def parse_subscript(self, node):
+ token = next(self.stream)
+ if token.type == 'dot':
+ attr_token = self.stream.current
+ next(self.stream)
+ if attr_token.type == 'name':
+ return nodes.Getattr(node, attr_token.value, 'load',
+ lineno=token.lineno)
+ elif attr_token.type != 'integer':
+ self.fail('expected name or number', attr_token.lineno)
+ arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
+ return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
+ if token.type == 'lbracket':
+ args = []
+ while self.stream.current.type != 'rbracket':
+ if args:
+ self.stream.expect('comma')
+ args.append(self.parse_subscribed())
+ self.stream.expect('rbracket')
+ if len(args) == 1:
+ arg = args[0]
+ else:
+ arg = nodes.Tuple(args, 'load', lineno=token.lineno)
+ return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
+ self.fail('expected subscript expression', self.lineno)
+
+ def parse_subscribed(self):
+ lineno = self.stream.current.lineno
+
+ if self.stream.current.type == 'colon':
+ next(self.stream)
+ args = [None]
+ else:
+ node = self.parse_expression()
+ if self.stream.current.type != 'colon':
+ return node
+ next(self.stream)
+ args = [node]
+
+ if self.stream.current.type == 'colon':
+ args.append(None)
+ elif self.stream.current.type not in ('rbracket', 'comma'):
+ args.append(self.parse_expression())
+ else:
+ args.append(None)
+
+ if self.stream.current.type == 'colon':
+ next(self.stream)
+ if self.stream.current.type not in ('rbracket', 'comma'):
+ args.append(self.parse_expression())
+ else:
+ args.append(None)
+ else:
+ args.append(None)
+
+ return nodes.Slice(lineno=lineno, *args)
+
+ def parse_call(self, node):
+ token = self.stream.expect('lparen')
+ args = []
+ kwargs = []
+ dyn_args = dyn_kwargs = None
+ require_comma = False
+
+ def ensure(expr):
+ if not expr:
+ self.fail('invalid syntax for function call expression',
+ token.lineno)
+
+ while self.stream.current.type != 'rparen':
+ if require_comma:
+ self.stream.expect('comma')
+ # support for trailing comma
+ if self.stream.current.type == 'rparen':
+ break
+ if self.stream.current.type == 'mul':
+ ensure(dyn_args is None and dyn_kwargs is None)
+ next(self.stream)
+ dyn_args = self.parse_expression()
+ elif self.stream.current.type == 'pow':
+ ensure(dyn_kwargs is None)
+ next(self.stream)
+ dyn_kwargs = self.parse_expression()
+ else:
+ ensure(dyn_args is None and dyn_kwargs is None)
+ if self.stream.current.type == 'name' and \
+ self.stream.look().type == 'assign':
+ key = self.stream.current.value
+ self.stream.skip(2)
+ value = self.parse_expression()
+ kwargs.append(nodes.Keyword(key, value,
+ lineno=value.lineno))
+ else:
+ ensure(not kwargs)
+ args.append(self.parse_expression())
+
+ require_comma = True
+ self.stream.expect('rparen')
+
+ if node is None:
+ return args, kwargs, dyn_args, dyn_kwargs
+ return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
+ lineno=token.lineno)
+
+ def parse_filter(self, node, start_inline=False):
+ while self.stream.current.type == 'pipe' or start_inline:
+ if not start_inline:
+ next(self.stream)
+ token = self.stream.expect('name')
+ name = token.value
+ while self.stream.current.type == 'dot':
+ next(self.stream)
+ name += '.' + self.stream.expect('name').value
+ if self.stream.current.type == 'lparen':
+ args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
+ else:
+ args = []
+ kwargs = []
+ dyn_args = dyn_kwargs = None
+ node = nodes.Filter(node, name, args, kwargs, dyn_args,
+ dyn_kwargs, lineno=token.lineno)
+ start_inline = False
+ return node
+
+ def parse_test(self, node):
+ token = next(self.stream)
+ if self.stream.current.test('name:not'):
+ next(self.stream)
+ negated = True
+ else:
+ negated = False
+ name = self.stream.expect('name').value
+ while self.stream.current.type == 'dot':
+ next(self.stream)
+ name += '.' + self.stream.expect('name').value
+ dyn_args = dyn_kwargs = None
+ kwargs = []
+ if self.stream.current.type == 'lparen':
+ args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
+ elif self.stream.current.type in ('name', 'string', 'integer',
+ 'float', 'lparen', 'lbracket',
+ 'lbrace') and not \
+ self.stream.current.test_any('name:else', 'name:or',
+ 'name:and'):
+ if self.stream.current.test('name:is'):
+ self.fail('You cannot chain multiple tests with is')
+ args = [self.parse_expression()]
+ else:
+ args = []
+ node = nodes.Test(node, name, args, kwargs, dyn_args,
+ dyn_kwargs, lineno=token.lineno)
+ if negated:
+ node = nodes.Not(node, lineno=token.lineno)
+ return node
+
+ def subparse(self, end_tokens=None):
+ body = []
+ data_buffer = []
+ add_data = data_buffer.append
+
+ if end_tokens is not None:
+ self._end_token_stack.append(end_tokens)
+
+ def flush_data():
+ if data_buffer:
+ lineno = data_buffer[0].lineno
+ body.append(nodes.Output(data_buffer[:], lineno=lineno))
+ del data_buffer[:]
+
+ try:
+ while self.stream:
+ token = self.stream.current
+ if token.type == 'data':
+ if token.value:
+ add_data(nodes.TemplateData(token.value,
+ lineno=token.lineno))
+ next(self.stream)
+ elif token.type == 'variable_begin':
+ next(self.stream)
+ add_data(self.parse_tuple(with_condexpr=True))
+ self.stream.expect('variable_end')
+ elif token.type == 'block_begin':
+ flush_data()
+ next(self.stream)
+ if end_tokens is not None and \
+ self.stream.current.test_any(*end_tokens):
+ return body
+ rv = self.parse_statement()
+ if isinstance(rv, list):
+ body.extend(rv)
+ else:
+ body.append(rv)
+ self.stream.expect('block_end')
+ else:
+ raise AssertionError('internal parsing error')
+
+ flush_data()
+ finally:
+ if end_tokens is not None:
+ self._end_token_stack.pop()
+
+ return body
+
+ def parse(self):
+ """Parse the whole template into a `Template` node."""
+ result = nodes.Template(self.subparse(), lineno=1)
+ result.set_environment(self.environment)
+ return result
diff --git a/websdk/jinja2/runtime.py b/websdk/jinja2/runtime.py
new file mode 100644
index 0000000..a4a47a2
--- /dev/null
+++ b/websdk/jinja2/runtime.py
@@ -0,0 +1,548 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.runtime
+ ~~~~~~~~~~~~~~
+
+ Runtime helpers.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD.
+"""
+from itertools import chain, imap
+from jinja2.nodes import EvalContext, _context_function_types
+from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
+ concat, internalcode, next, object_type_repr
+from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
+ TemplateNotFound
+
+
+# these variables are exported to the template runtime
+__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
+ 'TemplateRuntimeError', 'missing', 'concat', 'escape',
+ 'markup_join', 'unicode_join', 'to_string', 'identity',
+ 'TemplateNotFound']
+
+#: the name of the function that is used to convert something into
+#: a string. 2to3 will adopt that automatically and the generated
+#: code can take advantage of it.
+to_string = unicode
+
+#: the identity function. Useful for certain things in the environment
+identity = lambda x: x
+
+
+def markup_join(seq):
+ """Concatenation that escapes if necessary and converts to unicode."""
+ buf = []
+ iterator = imap(soft_unicode, seq)
+ for arg in iterator:
+ buf.append(arg)
+ if hasattr(arg, '__html__'):
+ return Markup(u'').join(chain(buf, iterator))
+ return concat(buf)
+
+
+def unicode_join(seq):
+ """Simple args to unicode conversion and concatenation."""
+ return concat(imap(unicode, seq))
+
+
+def new_context(environment, template_name, blocks, vars=None,
+ shared=None, globals=None, locals=None):
+ """Internal helper to for context creation."""
+ if vars is None:
+ vars = {}
+ if shared:
+ parent = vars
+ else:
+ parent = dict(globals or (), **vars)
+ if locals:
+ # if the parent is shared a copy should be created because
+ # we don't want to modify the dict passed
+ if shared:
+ parent = dict(parent)
+ for key, value in locals.iteritems():
+ if key[:2] == 'l_' and value is not missing:
+ parent[key[2:]] = value
+ return Context(environment, parent, template_name, blocks)
+
+
+class TemplateReference(object):
+ """The `self` in templates."""
+
+ def __init__(self, context):
+ self.__context = context
+
+ def __getitem__(self, name):
+ blocks = self.__context.blocks[name]
+ return BlockReference(name, self.__context, blocks, 0)
+
+ def __repr__(self):
+ return '<%s %r>' % (
+ self.__class__.__name__,
+ self.__context.name
+ )
+
+
+class Context(object):
+ """The template context holds the variables of a template. It stores the
+ values passed to the template and also the names the template exports.
+ Creating instances is neither supported nor useful as it's created
+ automatically at various stages of the template evaluation and should not
+ be created by hand.
+
+ The context is immutable. Modifications on :attr:`parent` **must not**
+ happen and modifications on :attr:`vars` are allowed from generated
+ template code only. Template filters and global functions marked as
+ :func:`contextfunction`\s get the active context passed as first argument
+ and are allowed to access the context read-only.
+
+ The template context supports read only dict operations (`get`,
+ `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
+ `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
+ method that doesn't fail with a `KeyError` but returns an
+ :class:`Undefined` object for missing variables.
+ """
+ __slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars',
+ 'name', 'blocks', '__weakref__')
+
+ def __init__(self, environment, parent, name, blocks):
+ self.parent = parent
+ self.vars = {}
+ self.environment = environment
+ self.eval_ctx = EvalContext(self.environment, name)
+ self.exported_vars = set()
+ self.name = name
+
+ # create the initial mapping of blocks. Whenever template inheritance
+ # takes place the runtime will update this mapping with the new blocks
+ # from the template.
+ self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
+
+ def super(self, name, current):
+ """Render a parent block."""
+ try:
+ blocks = self.blocks[name]
+ index = blocks.index(current) + 1
+ blocks[index]
+ except LookupError:
+ return self.environment.undefined('there is no parent block '
+ 'called %r.' % name,
+ name='super')
+ return BlockReference(name, self, blocks, index)
+
+ def get(self, key, default=None):
+ """Returns an item from the template context, if it doesn't exist
+ `default` is returned.
+ """
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
+ def resolve(self, key):
+ """Looks up a variable like `__getitem__` or `get` but returns an
+ :class:`Undefined` object with the name of the name looked up.
+ """
+ if key in self.vars:
+ return self.vars[key]
+ if key in self.parent:
+ return self.parent[key]
+ return self.environment.undefined(name=key)
+
+ def get_exported(self):
+ """Get a new dict with the exported variables."""
+ return dict((k, self.vars[k]) for k in self.exported_vars)
+
+ def get_all(self):
+ """Return a copy of the complete context as dict including the
+ exported variables.
+ """
+ return dict(self.parent, **self.vars)
+
+ @internalcode
+ def call(__self, __obj, *args, **kwargs):
+ """Call the callable with the arguments and keyword arguments
+ provided but inject the active context or environment as first
+ argument if the callable is a :func:`contextfunction` or
+ :func:`environmentfunction`.
+ """
+ if __debug__:
+ __traceback_hide__ = True
+ if isinstance(__obj, _context_function_types):
+ if getattr(__obj, 'contextfunction', 0):
+ args = (__self,) + args
+ elif getattr(__obj, 'evalcontextfunction', 0):
+ args = (__self.eval_ctx,) + args
+ elif getattr(__obj, 'environmentfunction', 0):
+ args = (__self.environment,) + args
+ try:
+ return __obj(*args, **kwargs)
+ except StopIteration:
+ return __self.environment.undefined('value was undefined because '
+ 'a callable raised a '
+ 'StopIteration exception')
+
+ def derived(self, locals=None):
+ """Internal helper function to create a derived context."""
+ context = new_context(self.environment, self.name, {},
+ self.parent, True, None, locals)
+ context.vars.update(self.vars)
+ context.eval_ctx = self.eval_ctx
+ context.blocks.update((k, list(v)) for k, v in self.blocks.iteritems())
+ return context
+
+ def _all(meth):
+ proxy = lambda self: getattr(self.get_all(), meth)()
+ proxy.__doc__ = getattr(dict, meth).__doc__
+ proxy.__name__ = meth
+ return proxy
+
+ keys = _all('keys')
+ values = _all('values')
+ items = _all('items')
+
+ # not available on python 3
+ if hasattr(dict, 'iterkeys'):
+ iterkeys = _all('iterkeys')
+ itervalues = _all('itervalues')
+ iteritems = _all('iteritems')
+ del _all
+
+ def __contains__(self, name):
+ return name in self.vars or name in self.parent
+
+ def __getitem__(self, key):
+ """Lookup a variable or raise `KeyError` if the variable is
+ undefined.
+ """
+ item = self.resolve(key)
+ if isinstance(item, Undefined):
+ raise KeyError(key)
+ return item
+
+ def __repr__(self):
+ return '<%s %s of %r>' % (
+ self.__class__.__name__,
+ repr(self.get_all()),
+ self.name
+ )
+
+
+# register the context as mapping if possible
+try:
+ from collections import Mapping
+ Mapping.register(Context)
+except ImportError:
+ pass
+
+
+class BlockReference(object):
+ """One block on a template reference."""
+
+ def __init__(self, name, context, stack, depth):
+ self.name = name
+ self._context = context
+ self._stack = stack
+ self._depth = depth
+
+ @property
+ def super(self):
+ """Super the block."""
+ if self._depth + 1 >= len(self._stack):
+ return self._context.environment. \
+ undefined('there is no parent block called %r.' %
+ self.name, name='super')
+ return BlockReference(self.name, self._context, self._stack,
+ self._depth + 1)
+
+ @internalcode
+ def __call__(self):
+ rv = concat(self._stack[self._depth](self._context))
+ if self._context.eval_ctx.autoescape:
+ rv = Markup(rv)
+ return rv
+
+
+class LoopContext(object):
+ """A loop context for dynamic iteration."""
+
+ def __init__(self, iterable, recurse=None):
+ self._iterator = iter(iterable)
+ self._recurse = recurse
+ self.index0 = -1
+
+ # try to get the length of the iterable early. This must be done
+ # here because there are some broken iterators around where there
+ # __len__ is the number of iterations left (i'm looking at your
+ # listreverseiterator!).
+ try:
+ self._length = len(iterable)
+ except (TypeError, AttributeError):
+ self._length = None
+
+ def cycle(self, *args):
+ """Cycles among the arguments with the current loop index."""
+ if not args:
+ raise TypeError('no items for cycling given')
+ return args[self.index0 % len(args)]
+
+ first = property(lambda x: x.index0 == 0)
+ last = property(lambda x: x.index0 + 1 == x.length)
+ index = property(lambda x: x.index0 + 1)
+ revindex = property(lambda x: x.length - x.index0)
+ revindex0 = property(lambda x: x.length - x.index)
+
+ def __len__(self):
+ return self.length
+
+ def __iter__(self):
+ return LoopContextIterator(self)
+
+ @internalcode
+ def loop(self, iterable):
+ if self._recurse is None:
+ raise TypeError('Tried to call non recursive loop. Maybe you '
+ "forgot the 'recursive' modifier.")
+ return self._recurse(iterable, self._recurse)
+
+ # a nifty trick to enhance the error message if someone tried to call
+ # the the loop without or with too many arguments.
+ __call__ = loop
+ del loop
+
+ @property
+ def length(self):
+ if self._length is None:
+ # if was not possible to get the length of the iterator when
+ # the loop context was created (ie: iterating over a generator)
+ # we have to convert the iterable into a sequence and use the
+ # length of that.
+ iterable = tuple(self._iterator)
+ self._iterator = iter(iterable)
+ self._length = len(iterable) + self.index0 + 1
+ return self._length
+
+ def __repr__(self):
+ return '<%s %r/%r>' % (
+ self.__class__.__name__,
+ self.index,
+ self.length
+ )
+
+
+class LoopContextIterator(object):
+ """The iterator for a loop context."""
+ __slots__ = ('context',)
+
+ def __init__(self, context):
+ self.context = context
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ ctx = self.context
+ ctx.index0 += 1
+ return next(ctx._iterator), ctx
+
+
+class Macro(object):
+ """Wraps a macro function."""
+
+ def __init__(self, environment, func, name, arguments, defaults,
+ catch_kwargs, catch_varargs, caller):
+ self._environment = environment
+ self._func = func
+ self._argument_count = len(arguments)
+ self.name = name
+ self.arguments = arguments
+ self.defaults = defaults
+ self.catch_kwargs = catch_kwargs
+ self.catch_varargs = catch_varargs
+ self.caller = caller
+
+ @internalcode
+ def __call__(self, *args, **kwargs):
+ # try to consume the positional arguments
+ arguments = list(args[:self._argument_count])
+ off = len(arguments)
+
+ # if the number of arguments consumed is not the number of
+ # arguments expected we start filling in keyword arguments
+ # and defaults.
+ if off != self._argument_count:
+ for idx, name in enumerate(self.arguments[len(arguments):]):
+ try:
+ value = kwargs.pop(name)
+ except KeyError:
+ try:
+ value = self.defaults[idx - self._argument_count + off]
+ except IndexError:
+ value = self._environment.undefined(
+ 'parameter %r was not provided' % name, name=name)
+ arguments.append(value)
+
+ # it's important that the order of these arguments does not change
+ # if not also changed in the compiler's `function_scoping` method.
+ # the order is caller, keyword arguments, positional arguments!
+ if self.caller:
+ caller = kwargs.pop('caller', None)
+ if caller is None:
+ caller = self._environment.undefined('No caller defined',
+ name='caller')
+ arguments.append(caller)
+ if self.catch_kwargs:
+ arguments.append(kwargs)
+ elif kwargs:
+ raise TypeError('macro %r takes no keyword argument %r' %
+ (self.name, next(iter(kwargs))))
+ if self.catch_varargs:
+ arguments.append(args[self._argument_count:])
+ elif len(args) > self._argument_count:
+ raise TypeError('macro %r takes not more than %d argument(s)' %
+ (self.name, len(self.arguments)))
+ return self._func(*arguments)
+
+ def __repr__(self):
+ return '<%s %s>' % (
+ self.__class__.__name__,
+ self.name is None and 'anonymous' or repr(self.name)
+ )
+
+
+class Undefined(object):
+ """The default undefined type. This undefined type can be printed and
+ iterated over, but every other access will raise an :exc:`UndefinedError`:
+
+ >>> foo = Undefined(name='foo')
+ >>> str(foo)
+ ''
+ >>> not foo
+ True
+ >>> foo + 42
+ Traceback (most recent call last):
+ ...
+ UndefinedError: 'foo' is undefined
+ """
+ __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
+ '_undefined_exception')
+
+ def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError):
+ self._undefined_hint = hint
+ self._undefined_obj = obj
+ self._undefined_name = name
+ self._undefined_exception = exc
+
+ @internalcode
+ def _fail_with_undefined_error(self, *args, **kwargs):
+ """Regular callback function for undefined objects that raises an
+ `UndefinedError` on call.
+ """
+ if self._undefined_hint is None:
+ if self._undefined_obj is missing:
+ hint = '%r is undefined' % self._undefined_name
+ elif not isinstance(self._undefined_name, basestring):
+ hint = '%s has no element %r' % (
+ object_type_repr(self._undefined_obj),
+ self._undefined_name
+ )
+ else:
+ hint = '%r has no attribute %r' % (
+ object_type_repr(self._undefined_obj),
+ self._undefined_name
+ )
+ else:
+ hint = self._undefined_hint
+ raise self._undefined_exception(hint)
+
+ @internalcode
+ def __getattr__(self, name):
+ if name[:2] == '__':
+ raise AttributeError(name)
+ return self._fail_with_undefined_error()
+
+ __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
+ __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
+ __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
+ __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \
+ __float__ = __complex__ = __pow__ = __rpow__ = \
+ _fail_with_undefined_error
+
+ def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ # unicode goes after __str__ because we configured 2to3 to rename
+ # __unicode__ to __str__. because the 2to3 tree is not designed to
+ # remove nodes from it, we leave the above __str__ around and let
+ # it override at runtime.
+ def __unicode__(self):
+ return u''
+
+ def __len__(self):
+ return 0
+
+ def __iter__(self):
+ if 0:
+ yield None
+
+ def __nonzero__(self):
+ return False
+
+ def __repr__(self):
+ return 'Undefined'
+
+
+class DebugUndefined(Undefined):
+ """An undefined that returns the debug info when printed.
+
+ >>> foo = DebugUndefined(name='foo')
+ >>> str(foo)
+ '{{ foo }}'
+ >>> not foo
+ True
+ >>> foo + 42
+ Traceback (most recent call last):
+ ...
+ UndefinedError: 'foo' is undefined
+ """
+ __slots__ = ()
+
+ def __unicode__(self):
+ if self._undefined_hint is None:
+ if self._undefined_obj is missing:
+ return u'{{ %s }}' % self._undefined_name
+ return '{{ no such element: %s[%r] }}' % (
+ object_type_repr(self._undefined_obj),
+ self._undefined_name
+ )
+ return u'{{ undefined value printed: %s }}' % self._undefined_hint
+
+
+class StrictUndefined(Undefined):
+ """An undefined that barks on print and iteration as well as boolean
+ tests and all kinds of comparisons. In other words: you can do nothing
+ with it except checking if it's defined using the `defined` test.
+
+ >>> foo = StrictUndefined(name='foo')
+ >>> str(foo)
+ Traceback (most recent call last):
+ ...
+ UndefinedError: 'foo' is undefined
+ >>> not foo
+ Traceback (most recent call last):
+ ...
+ UndefinedError: 'foo' is undefined
+ >>> foo + 42
+ Traceback (most recent call last):
+ ...
+ UndefinedError: 'foo' is undefined
+ """
+ __slots__ = ()
+ __iter__ = __unicode__ = __str__ = __len__ = __nonzero__ = __eq__ = \
+ __ne__ = __bool__ = Undefined._fail_with_undefined_error
+
+
+# remove remaining slots attributes, after the metaclass did the magic they
+# are unneeded and irritating as they contain wrong data for the subclasses.
+del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__
diff --git a/websdk/jinja2/sandbox.py b/websdk/jinja2/sandbox.py
new file mode 100644
index 0000000..a1cbb29
--- /dev/null
+++ b/websdk/jinja2/sandbox.py
@@ -0,0 +1,361 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.sandbox
+ ~~~~~~~~~~~~~~
+
+ Adds a sandbox layer to Jinja as it was the default behavior in the old
+ Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the
+ default behavior is easier to use.
+
+ The behavior can be changed by subclassing the environment.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD.
+"""
+import operator
+from jinja2.environment import Environment
+from jinja2.exceptions import SecurityError
+from jinja2.utils import FunctionType, MethodType, TracebackType, CodeType, \
+ FrameType, GeneratorType
+
+
+#: maximum number of items a range may produce
+MAX_RANGE = 100000
+
+#: attributes of function objects that are considered unsafe.
+UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
+ 'func_defaults', 'func_globals'])
+
+#: unsafe method attributes. function attributes are unsafe for methods too
+UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
+
+
+import warnings
+
+# make sure we don't warn in python 2.6 about stuff we don't care about
+warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning,
+ module='jinja2.sandbox')
+
+from collections import deque
+
+_mutable_set_types = (set,)
+_mutable_mapping_types = (dict,)
+_mutable_sequence_types = (list,)
+
+
+# on python 2.x we can register the user collection types
+try:
+ from UserDict import UserDict, DictMixin
+ from UserList import UserList
+ _mutable_mapping_types += (UserDict, DictMixin)
+ _mutable_set_types += (UserList,)
+except ImportError:
+ pass
+
+# if sets is still available, register the mutable set from there as well
+try:
+ from sets import Set
+ _mutable_set_types += (Set,)
+except ImportError:
+ pass
+
+#: register Python 2.6 abstract base classes
+try:
+ from collections import MutableSet, MutableMapping, MutableSequence
+ _mutable_set_types += (MutableSet,)
+ _mutable_mapping_types += (MutableMapping,)
+ _mutable_sequence_types += (MutableSequence,)
+except ImportError:
+ pass
+
+_mutable_spec = (
+ (_mutable_set_types, frozenset([
+ 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
+ 'symmetric_difference_update', 'update'
+ ])),
+ (_mutable_mapping_types, frozenset([
+ 'clear', 'pop', 'popitem', 'setdefault', 'update'
+ ])),
+ (_mutable_sequence_types, frozenset([
+ 'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
+ ])),
+ (deque, frozenset([
+ 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
+ 'popleft', 'remove', 'rotate'
+ ]))
+)
+
+
+def safe_range(*args):
+ """A range that can't generate ranges with a length of more than
+ MAX_RANGE items.
+ """
+ rng = xrange(*args)
+ if len(rng) > MAX_RANGE:
+ raise OverflowError('range too big, maximum size for range is %d' %
+ MAX_RANGE)
+ return rng
+
+
+def unsafe(f):
+ """Marks a function or method as unsafe.
+
+ ::
+
+ @unsafe
+ def delete(self):
+ pass
+ """
+ f.unsafe_callable = True
+ return f
+
+
+def is_internal_attribute(obj, attr):
+ """Test if the attribute given is an internal python attribute. For
+ example this function returns `True` for the `func_code` attribute of
+ python objects. This is useful if the environment method
+ :meth:`~SandboxedEnvironment.is_safe_attribute` is overriden.
+
+ >>> from jinja2.sandbox import is_internal_attribute
+ >>> is_internal_attribute(lambda: None, "func_code")
+ True
+ >>> is_internal_attribute((lambda x:x).func_code, 'co_code')
+ True
+ >>> is_internal_attribute(str, "upper")
+ False
+ """
+ if isinstance(obj, FunctionType):
+ if attr in UNSAFE_FUNCTION_ATTRIBUTES:
+ return True
+ elif isinstance(obj, MethodType):
+ if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
+ attr in UNSAFE_METHOD_ATTRIBUTES:
+ return True
+ elif isinstance(obj, type):
+ if attr == 'mro':
+ return True
+ elif isinstance(obj, (CodeType, TracebackType, FrameType)):
+ return True
+ elif isinstance(obj, GeneratorType):
+ if attr == 'gi_frame':
+ return True
+ return attr.startswith('__')
+
+
+def modifies_known_mutable(obj, attr):
+ """This function checks if an attribute on a builtin mutable object
+ (list, dict, set or deque) would modify it if called. It also supports
+ the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
+ with Python 2.6 onwards the abstract base classes `MutableSet`,
+ `MutableMapping`, and `MutableSequence`.
+
+ >>> modifies_known_mutable({}, "clear")
+ True
+ >>> modifies_known_mutable({}, "keys")
+ False
+ >>> modifies_known_mutable([], "append")
+ True
+ >>> modifies_known_mutable([], "index")
+ False
+
+ If called with an unsupported object (such as unicode) `False` is
+ returned.
+
+ >>> modifies_known_mutable("foo", "upper")
+ False
+ """
+ for typespec, unsafe in _mutable_spec:
+ if isinstance(obj, typespec):
+ return attr in unsafe
+ return False
+
+
+class SandboxedEnvironment(Environment):
+ """The sandboxed environment. It works like the regular environment but
+ tells the compiler to generate sandboxed code. Additionally subclasses of
+ this environment may override the methods that tell the runtime what
+ attributes or functions are safe to access.
+
+ If the template tries to access insecure code a :exc:`SecurityError` is
+ raised. However also other exceptions may occour during the rendering so
+ the caller has to ensure that all exceptions are catched.
+ """
+ sandboxed = True
+
+ #: default callback table for the binary operators. A copy of this is
+ #: available on each instance of a sandboxed environment as
+ #: :attr:`binop_table`
+ default_binop_table = {
+ '+': operator.add,
+ '-': operator.sub,
+ '*': operator.mul,
+ '/': operator.truediv,
+ '//': operator.floordiv,
+ '**': operator.pow,
+ '%': operator.mod
+ }
+
+ #: default callback table for the unary operators. A copy of this is
+ #: available on each instance of a sandboxed environment as
+ #: :attr:`unop_table`
+ default_unop_table = {
+ '+': operator.pos,
+ '-': operator.neg
+ }
+
+ #: a set of binary operators that should be intercepted. Each operator
+ #: that is added to this set (empty by default) is delegated to the
+ #: :meth:`call_binop` method that will perform the operator. The default
+ #: operator callback is specified by :attr:`binop_table`.
+ #:
+ #: The following binary operators are interceptable:
+ #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**``
+ #:
+ #: The default operation form the operator table corresponds to the
+ #: builtin function. Intercepted calls are always slower than the native
+ #: operator call, so make sure only to intercept the ones you are
+ #: interested in.
+ #:
+ #: .. versionadded:: 2.6
+ intercepted_binops = frozenset()
+
+ #: a set of unary operators that should be intercepted. Each operator
+ #: that is added to this set (empty by default) is delegated to the
+ #: :meth:`call_unop` method that will perform the operator. The default
+ #: operator callback is specified by :attr:`unop_table`.
+ #:
+ #: The following unary operators are interceptable: ``+``, ``-``
+ #:
+ #: The default operation form the operator table corresponds to the
+ #: builtin function. Intercepted calls are always slower than the native
+ #: operator call, so make sure only to intercept the ones you are
+ #: interested in.
+ #:
+ #: .. versionadded:: 2.6
+ intercepted_unops = frozenset()
+
+ def intercept_unop(self, operator):
+ """Called during template compilation with the name of a unary
+ operator to check if it should be intercepted at runtime. If this
+ method returns `True`, :meth:`call_unop` is excuted for this unary
+ operator. The default implementation of :meth:`call_unop` will use
+ the :attr:`unop_table` dictionary to perform the operator with the
+ same logic as the builtin one.
+
+ The following unary operators are interceptable: ``+`` and ``-``
+
+ Intercepted calls are always slower than the native operator call,
+ so make sure only to intercept the ones you are interested in.
+
+ .. versionadded:: 2.6
+ """
+ return False
+
+
+ def __init__(self, *args, **kwargs):
+ Environment.__init__(self, *args, **kwargs)
+ self.globals['range'] = safe_range
+ self.binop_table = self.default_binop_table.copy()
+ self.unop_table = self.default_unop_table.copy()
+
+ def is_safe_attribute(self, obj, attr, value):
+ """The sandboxed environment will call this method to check if the
+ attribute of an object is safe to access. Per default all attributes
+ starting with an underscore are considered private as well as the
+ special attributes of internal python objects as returned by the
+ :func:`is_internal_attribute` function.
+ """
+ return not (attr.startswith('_') or is_internal_attribute(obj, attr))
+
+ def is_safe_callable(self, obj):
+ """Check if an object is safely callable. Per default a function is
+ considered safe unless the `unsafe_callable` attribute exists and is
+ True. Override this method to alter the behavior, but this won't
+ affect the `unsafe` decorator from this module.
+ """
+ return not (getattr(obj, 'unsafe_callable', False) or
+ getattr(obj, 'alters_data', False))
+
+ def call_binop(self, context, operator, left, right):
+ """For intercepted binary operator calls (:meth:`intercepted_binops`)
+ this function is executed instead of the builtin operator. This can
+ be used to fine tune the behavior of certain operators.
+
+ .. versionadded:: 2.6
+ """
+ return self.binop_table[operator](left, right)
+
+ def call_unop(self, context, operator, arg):
+ """For intercepted unary operator calls (:meth:`intercepted_unops`)
+ this function is executed instead of the builtin operator. This can
+ be used to fine tune the behavior of certain operators.
+
+ .. versionadded:: 2.6
+ """
+ return self.unop_table[operator](arg)
+
+ def getitem(self, obj, argument):
+ """Subscribe an object from sandboxed code."""
+ try:
+ return obj[argument]
+ except (TypeError, LookupError):
+ if isinstance(argument, basestring):
+ try:
+ attr = str(argument)
+ except Exception:
+ pass
+ else:
+ try:
+ value = getattr(obj, attr)
+ except AttributeError:
+ pass
+ else:
+ if self.is_safe_attribute(obj, argument, value):
+ return value
+ return self.unsafe_undefined(obj, argument)
+ return self.undefined(obj=obj, name=argument)
+
+ def getattr(self, obj, attribute):
+ """Subscribe an object from sandboxed code and prefer the
+ attribute. The attribute passed *must* be a bytestring.
+ """
+ try:
+ value = getattr(obj, attribute)
+ except AttributeError:
+ try:
+ return obj[attribute]
+ except (TypeError, LookupError):
+ pass
+ else:
+ if self.is_safe_attribute(obj, attribute, value):
+ return value
+ return self.unsafe_undefined(obj, attribute)
+ return self.undefined(obj=obj, name=attribute)
+
+ def unsafe_undefined(self, obj, attribute):
+ """Return an undefined object for unsafe attributes."""
+ return self.undefined('access to attribute %r of %r '
+ 'object is unsafe.' % (
+ attribute,
+ obj.__class__.__name__
+ ), name=attribute, obj=obj, exc=SecurityError)
+
+ def call(__self, __context, __obj, *args, **kwargs):
+ """Call an object from sandboxed code."""
+ # the double prefixes are to avoid double keyword argument
+ # errors when proxying the call.
+ if not __self.is_safe_callable(__obj):
+ raise SecurityError('%r is not safely callable' % (__obj,))
+ return __context.call(__obj, *args, **kwargs)
+
+
+class ImmutableSandboxedEnvironment(SandboxedEnvironment):
+ """Works exactly like the regular `SandboxedEnvironment` but does not
+ permit modifications on the builtin mutable objects `list`, `set`, and
+ `dict` by using the :func:`modifies_known_mutable` function.
+ """
+
+ def is_safe_attribute(self, obj, attr, value):
+ if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
+ return False
+ return not modifies_known_mutable(obj, attr)
diff --git a/websdk/jinja2/tests.py b/websdk/jinja2/tests.py
new file mode 100644
index 0000000..50510b0
--- /dev/null
+++ b/websdk/jinja2/tests.py
@@ -0,0 +1,161 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.tests
+ ~~~~~~~~~~~~
+
+ Jinja test functions. Used with the "is" operator.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+from jinja2.runtime import Undefined
+
+try:
+ from collections import Mapping as MappingType
+except ImportError:
+ import UserDict
+ MappingType = (UserDict.UserDict, UserDict.DictMixin, dict)
+
+# nose, nothing here to test
+__test__ = False
+
+
+number_re = re.compile(r'^-?\d+(\.\d+)?$')
+regex_type = type(number_re)
+
+
+try:
+ test_callable = callable
+except NameError:
+ def test_callable(x):
+ return hasattr(x, '__call__')
+
+
+def test_odd(value):
+ """Return true if the variable is odd."""
+ return value % 2 == 1
+
+
+def test_even(value):
+ """Return true if the variable is even."""
+ return value % 2 == 0
+
+
+def test_divisibleby(value, num):
+ """Check if a variable is divisible by a number."""
+ return value % num == 0
+
+
+def test_defined(value):
+ """Return true if the variable is defined:
+
+ .. sourcecode:: jinja
+
+ {% if variable is defined %}
+ value of variable: {{ variable }}
+ {% else %}
+ variable is not defined
+ {% endif %}
+
+ See the :func:`default` filter for a simple way to set undefined
+ variables.
+ """
+ return not isinstance(value, Undefined)
+
+
+def test_undefined(value):
+ """Like :func:`defined` but the other way round."""
+ return isinstance(value, Undefined)
+
+
+def test_none(value):
+ """Return true if the variable is none."""
+ return value is None
+
+
+def test_lower(value):
+ """Return true if the variable is lowercased."""
+ return unicode(value).islower()
+
+
+def test_upper(value):
+ """Return true if the variable is uppercased."""
+ return unicode(value).isupper()
+
+
+def test_string(value):
+ """Return true if the object is a string."""
+ return isinstance(value, basestring)
+
+
+def test_mapping(value):
+ """Return true if the object is a mapping (dict etc.).
+
+ .. versionadded:: 2.6
+ """
+ return isinstance(value, MappingType)
+
+
+def test_number(value):
+ """Return true if the variable is a number."""
+ return isinstance(value, (int, long, float, complex))
+
+
+def test_sequence(value):
+ """Return true if the variable is a sequence. Sequences are variables
+ that are iterable.
+ """
+ try:
+ len(value)
+ value.__getitem__
+ except:
+ return False
+ return True
+
+
+def test_sameas(value, other):
+ """Check if an object points to the same memory address than another
+ object:
+
+ .. sourcecode:: jinja
+
+ {% if foo.attribute is sameas false %}
+ the foo attribute really is the `False` singleton
+ {% endif %}
+ """
+ return value is other
+
+
+def test_iterable(value):
+ """Check if it's possible to iterate over an object."""
+ try:
+ iter(value)
+ except TypeError:
+ return False
+ return True
+
+
+def test_escaped(value):
+ """Check if the value is escaped."""
+ return hasattr(value, '__html__')
+
+
+TESTS = {
+ 'odd': test_odd,
+ 'even': test_even,
+ 'divisibleby': test_divisibleby,
+ 'defined': test_defined,
+ 'undefined': test_undefined,
+ 'none': test_none,
+ 'lower': test_lower,
+ 'upper': test_upper,
+ 'string': test_string,
+ 'mapping': test_mapping,
+ 'number': test_number,
+ 'sequence': test_sequence,
+ 'iterable': test_iterable,
+ 'callable': test_callable,
+ 'sameas': test_sameas,
+ 'escaped': test_escaped
+}
diff --git a/websdk/jinja2/testsuite/__init__.py b/websdk/jinja2/testsuite/__init__.py
new file mode 100644
index 0000000..1f10ef6
--- /dev/null
+++ b/websdk/jinja2/testsuite/__init__.py
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite
+ ~~~~~~~~~~~~~~~~
+
+ All the unittests of Jinja2. These tests can be executed by
+ either running run-tests.py using multiple Python versions at
+ the same time.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import re
+import sys
+import unittest
+from traceback import format_exception
+from jinja2 import loaders
+
+
+here = os.path.dirname(os.path.abspath(__file__))
+
+dict_loader = loaders.DictLoader({
+ 'justdict.html': 'FOO'
+})
+package_loader = loaders.PackageLoader('jinja2.testsuite.res', 'templates')
+filesystem_loader = loaders.FileSystemLoader(here + '/res/templates')
+function_loader = loaders.FunctionLoader({'justfunction.html': 'FOO'}.get)
+choice_loader = loaders.ChoiceLoader([dict_loader, package_loader])
+prefix_loader = loaders.PrefixLoader({
+ 'a': filesystem_loader,
+ 'b': dict_loader
+})
+
+
+class JinjaTestCase(unittest.TestCase):
+
+ ### use only these methods for testing. If you need standard
+ ### unittest method, wrap them!
+
+ def setup(self):
+ pass
+
+ def teardown(self):
+ pass
+
+ def setUp(self):
+ self.setup()
+
+ def tearDown(self):
+ self.teardown()
+
+ def assert_equal(self, a, b):
+ return self.assertEqual(a, b)
+
+ def assert_raises(self, *args, **kwargs):
+ return self.assertRaises(*args, **kwargs)
+
+ def assert_traceback_matches(self, callback, expected_tb):
+ try:
+ callback()
+ except Exception, e:
+ tb = format_exception(*sys.exc_info())
+ if re.search(expected_tb.strip(), ''.join(tb)) is None:
+ raise self.fail('Traceback did not match:\n\n%s\nexpected:\n%s'
+ % (''.join(tb), expected_tb))
+ else:
+ self.fail('Expected exception')
+
+
+def suite():
+ from jinja2.testsuite import ext, filters, tests, core_tags, \
+ loader, inheritance, imports, lexnparse, security, api, \
+ regression, debug, utils, doctests
+ suite = unittest.TestSuite()
+ suite.addTest(ext.suite())
+ suite.addTest(filters.suite())
+ suite.addTest(tests.suite())
+ suite.addTest(core_tags.suite())
+ suite.addTest(loader.suite())
+ suite.addTest(inheritance.suite())
+ suite.addTest(imports.suite())
+ suite.addTest(lexnparse.suite())
+ suite.addTest(security.suite())
+ suite.addTest(api.suite())
+ suite.addTest(regression.suite())
+ suite.addTest(debug.suite())
+ suite.addTest(utils.suite())
+
+ # doctests will not run on python 3 currently. Too many issues
+ # with that, do not test that on that platform.
+ if sys.version_info < (3, 0):
+ suite.addTest(doctests.suite())
+
+ return suite
diff --git a/websdk/jinja2/testsuite/api.py b/websdk/jinja2/testsuite/api.py
new file mode 100644
index 0000000..c8f9634
--- /dev/null
+++ b/websdk/jinja2/testsuite/api.py
@@ -0,0 +1,245 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.api
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Tests the public API and related stuff.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Environment, Undefined, DebugUndefined, \
+ StrictUndefined, UndefinedError, meta, \
+ is_undefined, Template, DictLoader
+from jinja2.utils import Cycler
+
+env = Environment()
+
+
+class ExtendedAPITestCase(JinjaTestCase):
+
+ def test_item_and_attribute(self):
+ from jinja2.sandbox import SandboxedEnvironment
+
+ for env in Environment(), SandboxedEnvironment():
+ # the |list is necessary for python3
+ tmpl = env.from_string('{{ foo.items()|list }}')
+ assert tmpl.render(foo={'items': 42}) == "[('items', 42)]"
+ tmpl = env.from_string('{{ foo|attr("items")()|list }}')
+ assert tmpl.render(foo={'items': 42}) == "[('items', 42)]"
+ tmpl = env.from_string('{{ foo["items"] }}')
+ assert tmpl.render(foo={'items': 42}) == '42'
+
+ def test_finalizer(self):
+ def finalize_none_empty(value):
+ if value is None:
+ value = u''
+ return value
+ env = Environment(finalize=finalize_none_empty)
+ tmpl = env.from_string('{% for item in seq %}|{{ item }}{% endfor %}')
+ assert tmpl.render(seq=(None, 1, "foo")) == '||1|foo'
+ tmpl = env.from_string('<{{ none }}>')
+ assert tmpl.render() == '<>'
+
+ def test_cycler(self):
+ items = 1, 2, 3
+ c = Cycler(*items)
+ for item in items + items:
+ assert c.current == item
+ assert c.next() == item
+ c.next()
+ assert c.current == 2
+ c.reset()
+ assert c.current == 1
+
+ def test_expressions(self):
+ expr = env.compile_expression("foo")
+ assert expr() is None
+ assert expr(foo=42) == 42
+ expr2 = env.compile_expression("foo", undefined_to_none=False)
+ assert is_undefined(expr2())
+
+ expr = env.compile_expression("42 + foo")
+ assert expr(foo=42) == 84
+
+ def test_template_passthrough(self):
+ t = Template('Content')
+ assert env.get_template(t) is t
+ assert env.select_template([t]) is t
+ assert env.get_or_select_template([t]) is t
+ assert env.get_or_select_template(t) is t
+
+ def test_autoescape_autoselect(self):
+ def select_autoescape(name):
+ if name is None or '.' not in name:
+ return False
+ return name.endswith('.html')
+ env = Environment(autoescape=select_autoescape,
+ loader=DictLoader({
+ 'test.txt': '{{ foo }}',
+ 'test.html': '{{ foo }}'
+ }))
+ t = env.get_template('test.txt')
+ assert t.render(foo='<foo>') == '<foo>'
+ t = env.get_template('test.html')
+ assert t.render(foo='<foo>') == '&lt;foo&gt;'
+ t = env.from_string('{{ foo }}')
+ assert t.render(foo='<foo>') == '<foo>'
+
+
+class MetaTestCase(JinjaTestCase):
+
+ def test_find_undeclared_variables(self):
+ ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
+ x = meta.find_undeclared_variables(ast)
+ assert x == set(['bar'])
+
+ ast = env.parse('{% set foo = 42 %}{{ bar + foo }}'
+ '{% macro meh(x) %}{{ x }}{% endmacro %}'
+ '{% for item in seq %}{{ muh(item) + meh(seq) }}{% endfor %}')
+ x = meta.find_undeclared_variables(ast)
+ assert x == set(['bar', 'seq', 'muh'])
+
+ def test_find_refererenced_templates(self):
+ ast = env.parse('{% extends "layout.html" %}{% include helper %}')
+ i = meta.find_referenced_templates(ast)
+ assert i.next() == 'layout.html'
+ assert i.next() is None
+ assert list(i) == []
+
+ ast = env.parse('{% extends "layout.html" %}'
+ '{% from "test.html" import a, b as c %}'
+ '{% import "meh.html" as meh %}'
+ '{% include "muh.html" %}')
+ i = meta.find_referenced_templates(ast)
+ assert list(i) == ['layout.html', 'test.html', 'meh.html', 'muh.html']
+
+ def test_find_included_templates(self):
+ ast = env.parse('{% include ["foo.html", "bar.html"] %}')
+ i = meta.find_referenced_templates(ast)
+ assert list(i) == ['foo.html', 'bar.html']
+
+ ast = env.parse('{% include ("foo.html", "bar.html") %}')
+ i = meta.find_referenced_templates(ast)
+ assert list(i) == ['foo.html', 'bar.html']
+
+ ast = env.parse('{% include ["foo.html", "bar.html", foo] %}')
+ i = meta.find_referenced_templates(ast)
+ assert list(i) == ['foo.html', 'bar.html', None]
+
+ ast = env.parse('{% include ("foo.html", "bar.html", foo) %}')
+ i = meta.find_referenced_templates(ast)
+ assert list(i) == ['foo.html', 'bar.html', None]
+
+
+class StreamingTestCase(JinjaTestCase):
+
+ def test_basic_streaming(self):
+ tmpl = env.from_string("<ul>{% for item in seq %}<li>{{ loop.index "
+ "}} - {{ item }}</li>{%- endfor %}</ul>")
+ stream = tmpl.stream(seq=range(4))
+ self.assert_equal(stream.next(), '<ul>')
+ self.assert_equal(stream.next(), '<li>1 - 0</li>')
+ self.assert_equal(stream.next(), '<li>2 - 1</li>')
+ self.assert_equal(stream.next(), '<li>3 - 2</li>')
+ self.assert_equal(stream.next(), '<li>4 - 3</li>')
+ self.assert_equal(stream.next(), '</ul>')
+
+ def test_buffered_streaming(self):
+ tmpl = env.from_string("<ul>{% for item in seq %}<li>{{ loop.index "
+ "}} - {{ item }}</li>{%- endfor %}</ul>")
+ stream = tmpl.stream(seq=range(4))
+ stream.enable_buffering(size=3)
+ self.assert_equal(stream.next(), u'<ul><li>1 - 0</li><li>2 - 1</li>')
+ self.assert_equal(stream.next(), u'<li>3 - 2</li><li>4 - 3</li></ul>')
+
+ def test_streaming_behavior(self):
+ tmpl = env.from_string("")
+ stream = tmpl.stream()
+ assert not stream.buffered
+ stream.enable_buffering(20)
+ assert stream.buffered
+ stream.disable_buffering()
+ assert not stream.buffered
+
+
+class UndefinedTestCase(JinjaTestCase):
+
+ def test_stopiteration_is_undefined(self):
+ def test():
+ raise StopIteration()
+ t = Template('A{{ test() }}B')
+ assert t.render(test=test) == 'AB'
+ t = Template('A{{ test().missingattribute }}B')
+ self.assert_raises(UndefinedError, t.render, test=test)
+
+ def test_undefined_and_special_attributes(self):
+ try:
+ Undefined('Foo').__dict__
+ except AttributeError:
+ pass
+ else:
+ assert False, "Expected actual attribute error"
+
+ def test_default_undefined(self):
+ env = Environment(undefined=Undefined)
+ self.assert_equal(env.from_string('{{ missing }}').render(), u'')
+ self.assert_raises(UndefinedError,
+ env.from_string('{{ missing.attribute }}').render)
+ self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]')
+ self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True')
+ self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42), '')
+ self.assert_equal(env.from_string('{{ not missing }}').render(), 'True')
+
+ def test_debug_undefined(self):
+ env = Environment(undefined=DebugUndefined)
+ self.assert_equal(env.from_string('{{ missing }}').render(), '{{ missing }}')
+ self.assert_raises(UndefinedError,
+ env.from_string('{{ missing.attribute }}').render)
+ self.assert_equal(env.from_string('{{ missing|list }}').render(), '[]')
+ self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True')
+ self.assert_equal(env.from_string('{{ foo.missing }}').render(foo=42),
+ u"{{ no such element: int object['missing'] }}")
+ self.assert_equal(env.from_string('{{ not missing }}').render(), 'True')
+
+ def test_strict_undefined(self):
+ env = Environment(undefined=StrictUndefined)
+ self.assert_raises(UndefinedError, env.from_string('{{ missing }}').render)
+ self.assert_raises(UndefinedError, env.from_string('{{ missing.attribute }}').render)
+ self.assert_raises(UndefinedError, env.from_string('{{ missing|list }}').render)
+ self.assert_equal(env.from_string('{{ missing is not defined }}').render(), 'True')
+ self.assert_raises(UndefinedError, env.from_string('{{ foo.missing }}').render, foo=42)
+ self.assert_raises(UndefinedError, env.from_string('{{ not missing }}').render)
+
+ def test_indexing_gives_undefined(self):
+ t = Template("{{ var[42].foo }}")
+ self.assert_raises(UndefinedError, t.render, var=0)
+
+ def test_none_gives_proper_error(self):
+ try:
+ Environment().getattr(None, 'split')()
+ except UndefinedError, e:
+ assert e.message == "'None' has no attribute 'split'"
+ else:
+ assert False, 'expected exception'
+
+ def test_object_repr(self):
+ try:
+ Undefined(obj=42, name='upper')()
+ except UndefinedError, e:
+ assert e.message == "'int object' has no attribute 'upper'"
+ else:
+ assert False, 'expected exception'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ExtendedAPITestCase))
+ suite.addTest(unittest.makeSuite(MetaTestCase))
+ suite.addTest(unittest.makeSuite(StreamingTestCase))
+ suite.addTest(unittest.makeSuite(UndefinedTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/core_tags.py b/websdk/jinja2/testsuite/core_tags.py
new file mode 100644
index 0000000..2b5f580
--- /dev/null
+++ b/websdk/jinja2/testsuite/core_tags.py
@@ -0,0 +1,285 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.core_tags
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Test the core tags like for and if.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Environment, TemplateSyntaxError, UndefinedError, \
+ DictLoader
+
+env = Environment()
+
+
+class ForLoopTestCase(JinjaTestCase):
+
+ def test_simple(self):
+ tmpl = env.from_string('{% for item in seq %}{{ item }}{% endfor %}')
+ assert tmpl.render(seq=range(10)) == '0123456789'
+
+ def test_else(self):
+ tmpl = env.from_string('{% for item in seq %}XXX{% else %}...{% endfor %}')
+ assert tmpl.render() == '...'
+
+ def test_empty_blocks(self):
+ tmpl = env.from_string('<{% for item in seq %}{% else %}{% endfor %}>')
+ assert tmpl.render() == '<>'
+
+ def test_context_vars(self):
+ tmpl = env.from_string('''{% for item in seq -%}
+ {{ loop.index }}|{{ loop.index0 }}|{{ loop.revindex }}|{{
+ loop.revindex0 }}|{{ loop.first }}|{{ loop.last }}|{{
+ loop.length }}###{% endfor %}''')
+ one, two, _ = tmpl.render(seq=[0, 1]).split('###')
+ (one_index, one_index0, one_revindex, one_revindex0, one_first,
+ one_last, one_length) = one.split('|')
+ (two_index, two_index0, two_revindex, two_revindex0, two_first,
+ two_last, two_length) = two.split('|')
+
+ assert int(one_index) == 1 and int(two_index) == 2
+ assert int(one_index0) == 0 and int(two_index0) == 1
+ assert int(one_revindex) == 2 and int(two_revindex) == 1
+ assert int(one_revindex0) == 1 and int(two_revindex0) == 0
+ assert one_first == 'True' and two_first == 'False'
+ assert one_last == 'False' and two_last == 'True'
+ assert one_length == two_length == '2'
+
+ def test_cycling(self):
+ tmpl = env.from_string('''{% for item in seq %}{{
+ loop.cycle('<1>', '<2>') }}{% endfor %}{%
+ for item in seq %}{{ loop.cycle(*through) }}{% endfor %}''')
+ output = tmpl.render(seq=range(4), through=('<1>', '<2>'))
+ assert output == '<1><2>' * 4
+
+ def test_scope(self):
+ tmpl = env.from_string('{% for item in seq %}{% endfor %}{{ item }}')
+ output = tmpl.render(seq=range(10))
+ assert not output
+
+ def test_varlen(self):
+ def inner():
+ for item in range(5):
+ yield item
+ tmpl = env.from_string('{% for item in iter %}{{ item }}{% endfor %}')
+ output = tmpl.render(iter=inner())
+ assert output == '01234'
+
+ def test_noniter(self):
+ tmpl = env.from_string('{% for item in none %}...{% endfor %}')
+ self.assert_raises(TypeError, tmpl.render)
+
+ def test_recursive(self):
+ tmpl = env.from_string('''{% for item in seq recursive -%}
+ [{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
+ {%- endfor %}''')
+ assert tmpl.render(seq=[
+ dict(a=1, b=[dict(a=1), dict(a=2)]),
+ dict(a=2, b=[dict(a=1), dict(a=2)]),
+ dict(a=3, b=[dict(a='a')])
+ ]) == '[1<[1][2]>][2<[1][2]>][3<[a]>]'
+
+ def test_looploop(self):
+ tmpl = env.from_string('''{% for row in table %}
+ {%- set rowloop = loop -%}
+ {% for cell in row -%}
+ [{{ rowloop.index }}|{{ loop.index }}]
+ {%- endfor %}
+ {%- endfor %}''')
+ assert tmpl.render(table=['ab', 'cd']) == '[1|1][1|2][2|1][2|2]'
+
+ def test_reversed_bug(self):
+ tmpl = env.from_string('{% for i in items %}{{ i }}'
+ '{% if not loop.last %}'
+ ',{% endif %}{% endfor %}')
+ assert tmpl.render(items=reversed([3, 2, 1])) == '1,2,3'
+
+ def test_loop_errors(self):
+ tmpl = env.from_string('''{% for item in [1] if loop.index
+ == 0 %}...{% endfor %}''')
+ self.assert_raises(UndefinedError, tmpl.render)
+ tmpl = env.from_string('''{% for item in [] %}...{% else
+ %}{{ loop }}{% endfor %}''')
+ assert tmpl.render() == ''
+
+ def test_loop_filter(self):
+ tmpl = env.from_string('{% for item in range(10) if item '
+ 'is even %}[{{ item }}]{% endfor %}')
+ assert tmpl.render() == '[0][2][4][6][8]'
+ tmpl = env.from_string('''
+ {%- for item in range(10) if item is even %}[{{
+ loop.index }}:{{ item }}]{% endfor %}''')
+ assert tmpl.render() == '[1:0][2:2][3:4][4:6][5:8]'
+
+ def test_loop_unassignable(self):
+ self.assert_raises(TemplateSyntaxError, env.from_string,
+ '{% for loop in seq %}...{% endfor %}')
+
+ def test_scoped_special_var(self):
+ t = env.from_string('{% for s in seq %}[{{ loop.first }}{% for c in s %}'
+ '|{{ loop.first }}{% endfor %}]{% endfor %}')
+ assert t.render(seq=('ab', 'cd')) == '[True|True|False][False|True|False]'
+
+ def test_scoped_loop_var(self):
+ t = env.from_string('{% for x in seq %}{{ loop.first }}'
+ '{% for y in seq %}{% endfor %}{% endfor %}')
+ assert t.render(seq='ab') == 'TrueFalse'
+ t = env.from_string('{% for x in seq %}{% for y in seq %}'
+ '{{ loop.first }}{% endfor %}{% endfor %}')
+ assert t.render(seq='ab') == 'TrueFalseTrueFalse'
+
+ def test_recursive_empty_loop_iter(self):
+ t = env.from_string('''
+ {%- for item in foo recursive -%}{%- endfor -%}
+ ''')
+ assert t.render(dict(foo=[])) == ''
+
+ def test_call_in_loop(self):
+ t = env.from_string('''
+ {%- macro do_something() -%}
+ [{{ caller() }}]
+ {%- endmacro %}
+
+ {%- for i in [1, 2, 3] %}
+ {%- call do_something() -%}
+ {{ i }}
+ {%- endcall %}
+ {%- endfor -%}
+ ''')
+ assert t.render() == '[1][2][3]'
+
+ def test_scoping_bug(self):
+ t = env.from_string('''
+ {%- for item in foo %}...{{ item }}...{% endfor %}
+ {%- macro item(a) %}...{{ a }}...{% endmacro %}
+ {{- item(2) -}}
+ ''')
+ assert t.render(foo=(1,)) == '...1......2...'
+
+ def test_unpacking(self):
+ tmpl = env.from_string('{% for a, b, c in [[1, 2, 3]] %}'
+ '{{ a }}|{{ b }}|{{ c }}{% endfor %}')
+ assert tmpl.render() == '1|2|3'
+
+
+class IfConditionTestCase(JinjaTestCase):
+
+ def test_simple(self):
+ tmpl = env.from_string('''{% if true %}...{% endif %}''')
+ assert tmpl.render() == '...'
+
+ def test_elif(self):
+ tmpl = env.from_string('''{% if false %}XXX{% elif true
+ %}...{% else %}XXX{% endif %}''')
+ assert tmpl.render() == '...'
+
+ def test_else(self):
+ tmpl = env.from_string('{% if false %}XXX{% else %}...{% endif %}')
+ assert tmpl.render() == '...'
+
+ def test_empty(self):
+ tmpl = env.from_string('[{% if true %}{% else %}{% endif %}]')
+ assert tmpl.render() == '[]'
+
+ def test_complete(self):
+ tmpl = env.from_string('{% if a %}A{% elif b %}B{% elif c == d %}'
+ 'C{% else %}D{% endif %}')
+ assert tmpl.render(a=0, b=False, c=42, d=42.0) == 'C'
+
+ def test_no_scope(self):
+ tmpl = env.from_string('{% if a %}{% set foo = 1 %}{% endif %}{{ foo }}')
+ assert tmpl.render(a=True) == '1'
+ tmpl = env.from_string('{% if true %}{% set foo = 1 %}{% endif %}{{ foo }}')
+ assert tmpl.render() == '1'
+
+
+class MacrosTestCase(JinjaTestCase):
+ env = Environment(trim_blocks=True)
+
+ def test_simple(self):
+ tmpl = self.env.from_string('''\
+{% macro say_hello(name) %}Hello {{ name }}!{% endmacro %}
+{{ say_hello('Peter') }}''')
+ assert tmpl.render() == 'Hello Peter!'
+
+ def test_scoping(self):
+ tmpl = self.env.from_string('''\
+{% macro level1(data1) %}
+{% macro level2(data2) %}{{ data1 }}|{{ data2 }}{% endmacro %}
+{{ level2('bar') }}{% endmacro %}
+{{ level1('foo') }}''')
+ assert tmpl.render() == 'foo|bar'
+
+ def test_arguments(self):
+ tmpl = self.env.from_string('''\
+{% macro m(a, b, c='c', d='d') %}{{ a }}|{{ b }}|{{ c }}|{{ d }}{% endmacro %}
+{{ m() }}|{{ m('a') }}|{{ m('a', 'b') }}|{{ m(1, 2, 3) }}''')
+ assert tmpl.render() == '||c|d|a||c|d|a|b|c|d|1|2|3|d'
+
+ def test_varargs(self):
+ tmpl = self.env.from_string('''\
+{% macro test() %}{{ varargs|join('|') }}{% endmacro %}\
+{{ test(1, 2, 3) }}''')
+ assert tmpl.render() == '1|2|3'
+
+ def test_simple_call(self):
+ tmpl = self.env.from_string('''\
+{% macro test() %}[[{{ caller() }}]]{% endmacro %}\
+{% call test() %}data{% endcall %}''')
+ assert tmpl.render() == '[[data]]'
+
+ def test_complex_call(self):
+ tmpl = self.env.from_string('''\
+{% macro test() %}[[{{ caller('data') }}]]{% endmacro %}\
+{% call(data) test() %}{{ data }}{% endcall %}''')
+ assert tmpl.render() == '[[data]]'
+
+ def test_caller_undefined(self):
+ tmpl = self.env.from_string('''\
+{% set caller = 42 %}\
+{% macro test() %}{{ caller is not defined }}{% endmacro %}\
+{{ test() }}''')
+ assert tmpl.render() == 'True'
+
+ def test_include(self):
+ self.env = Environment(loader=DictLoader({'include':
+ '{% macro test(foo) %}[{{ foo }}]{% endmacro %}'}))
+ tmpl = self.env.from_string('{% from "include" import test %}{{ test("foo") }}')
+ assert tmpl.render() == '[foo]'
+
+ def test_macro_api(self):
+ tmpl = self.env.from_string('{% macro foo(a, b) %}{% endmacro %}'
+ '{% macro bar() %}{{ varargs }}{{ kwargs }}{% endmacro %}'
+ '{% macro baz() %}{{ caller() }}{% endmacro %}')
+ assert tmpl.module.foo.arguments == ('a', 'b')
+ assert tmpl.module.foo.defaults == ()
+ assert tmpl.module.foo.name == 'foo'
+ assert not tmpl.module.foo.caller
+ assert not tmpl.module.foo.catch_kwargs
+ assert not tmpl.module.foo.catch_varargs
+ assert tmpl.module.bar.arguments == ()
+ assert tmpl.module.bar.defaults == ()
+ assert not tmpl.module.bar.caller
+ assert tmpl.module.bar.catch_kwargs
+ assert tmpl.module.bar.catch_varargs
+ assert tmpl.module.baz.caller
+
+ def test_callself(self):
+ tmpl = self.env.from_string('{% macro foo(x) %}{{ x }}{% if x > 1 %}|'
+ '{{ foo(x - 1) }}{% endif %}{% endmacro %}'
+ '{{ foo(5) }}')
+ assert tmpl.render() == '5|4|3|2|1'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ForLoopTestCase))
+ suite.addTest(unittest.makeSuite(IfConditionTestCase))
+ suite.addTest(unittest.makeSuite(MacrosTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/debug.py b/websdk/jinja2/testsuite/debug.py
new file mode 100644
index 0000000..7552dec
--- /dev/null
+++ b/websdk/jinja2/testsuite/debug.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.debug
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the debug system.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+import unittest
+
+from jinja2.testsuite import JinjaTestCase, filesystem_loader
+
+from jinja2 import Environment, TemplateSyntaxError
+
+env = Environment(loader=filesystem_loader)
+
+
+class DebugTestCase(JinjaTestCase):
+
+ if sys.version_info[:2] != (2, 4):
+ def test_runtime_error(self):
+ def test():
+ tmpl.render(fail=lambda: 1 / 0)
+ tmpl = env.get_template('broken.html')
+ self.assert_traceback_matches(test, r'''
+ File ".*?broken.html", line 2, in (top-level template code|<module>)
+ \{\{ fail\(\) \}\}
+ File ".*?debug.pyc?", line \d+, in <lambda>
+ tmpl\.render\(fail=lambda: 1 / 0\)
+ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero
+''')
+
+ def test_syntax_error(self):
+ # XXX: the .*? is necessary for python3 which does not hide
+ # some of the stack frames we don't want to show. Not sure
+ # what's up with that, but that is not that critical. Should
+ # be fixed though.
+ self.assert_traceback_matches(lambda: env.get_template('syntaxerror.html'), r'''(?sm)
+ File ".*?syntaxerror.html", line 4, in (template|<module>)
+ \{% endif %\}.*?
+(jinja2\.exceptions\.)?TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja was looking for the following tags: 'endfor' or 'else'. The innermost block that needs to be closed is 'for'.
+ ''')
+
+ def test_regular_syntax_error(self):
+ def test():
+ raise TemplateSyntaxError('wtf', 42)
+ self.assert_traceback_matches(test, r'''
+ File ".*debug.pyc?", line \d+, in test
+ raise TemplateSyntaxError\('wtf', 42\)
+(jinja2\.exceptions\.)?TemplateSyntaxError: wtf
+ line 42''')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(DebugTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/doctests.py b/websdk/jinja2/testsuite/doctests.py
new file mode 100644
index 0000000..616d3b6
--- /dev/null
+++ b/websdk/jinja2/testsuite/doctests.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.doctests
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ The doctests. Collects all tests we want to test from
+ the Jinja modules.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+import doctest
+
+
+def suite():
+ from jinja2 import utils, sandbox, runtime, meta, loaders, \
+ ext, environment, bccache, nodes
+ suite = unittest.TestSuite()
+ suite.addTest(doctest.DocTestSuite(utils))
+ suite.addTest(doctest.DocTestSuite(sandbox))
+ suite.addTest(doctest.DocTestSuite(runtime))
+ suite.addTest(doctest.DocTestSuite(meta))
+ suite.addTest(doctest.DocTestSuite(loaders))
+ suite.addTest(doctest.DocTestSuite(ext))
+ suite.addTest(doctest.DocTestSuite(environment))
+ suite.addTest(doctest.DocTestSuite(bccache))
+ suite.addTest(doctest.DocTestSuite(nodes))
+ return suite
diff --git a/websdk/jinja2/testsuite/ext.py b/websdk/jinja2/testsuite/ext.py
new file mode 100644
index 0000000..6ca6c22
--- /dev/null
+++ b/websdk/jinja2/testsuite/ext.py
@@ -0,0 +1,455 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.ext
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Tests for the extensions.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+import unittest
+
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Environment, DictLoader, contextfunction, nodes
+from jinja2.exceptions import TemplateAssertionError
+from jinja2.ext import Extension
+from jinja2.lexer import Token, count_newlines
+from jinja2.utils import next
+
+# 2.x / 3.x
+try:
+ from io import BytesIO
+except ImportError:
+ from StringIO import StringIO as BytesIO
+
+
+importable_object = 23
+
+_gettext_re = re.compile(r'_\((.*?)\)(?s)')
+
+
+i18n_templates = {
+ 'master.html': '<title>{{ page_title|default(_("missing")) }}</title>'
+ '{% block body %}{% endblock %}',
+ 'child.html': '{% extends "master.html" %}{% block body %}'
+ '{% trans %}watch out{% endtrans %}{% endblock %}',
+ 'plural.html': '{% trans user_count %}One user online{% pluralize %}'
+ '{{ user_count }} users online{% endtrans %}',
+ 'stringformat.html': '{{ _("User: %(num)s")|format(num=user_count) }}'
+}
+
+newstyle_i18n_templates = {
+ 'master.html': '<title>{{ page_title|default(_("missing")) }}</title>'
+ '{% block body %}{% endblock %}',
+ 'child.html': '{% extends "master.html" %}{% block body %}'
+ '{% trans %}watch out{% endtrans %}{% endblock %}',
+ 'plural.html': '{% trans user_count %}One user online{% pluralize %}'
+ '{{ user_count }} users online{% endtrans %}',
+ 'stringformat.html': '{{ _("User: %(num)s", num=user_count) }}',
+ 'ngettext.html': '{{ ngettext("%(num)s apple", "%(num)s apples", apples) }}',
+ 'ngettext_long.html': '{% trans num=apples %}{{ num }} apple{% pluralize %}'
+ '{{ num }} apples{% endtrans %}',
+ 'transvars1.html': '{% trans %}User: {{ num }}{% endtrans %}',
+ 'transvars2.html': '{% trans num=count %}User: {{ num }}{% endtrans %}',
+ 'transvars3.html': '{% trans count=num %}User: {{ count }}{% endtrans %}',
+ 'novars.html': '{% trans %}%(hello)s{% endtrans %}',
+ 'vars.html': '{% trans %}{{ foo }}%(foo)s{% endtrans %}',
+ 'explicitvars.html': '{% trans foo="42" %}%(foo)s{% endtrans %}'
+}
+
+
+languages = {
+ 'de': {
+ 'missing': u'fehlend',
+ 'watch out': u'pass auf',
+ 'One user online': u'Ein Benutzer online',
+ '%(user_count)s users online': u'%(user_count)s Benutzer online',
+ 'User: %(num)s': u'Benutzer: %(num)s',
+ 'User: %(count)s': u'Benutzer: %(count)s',
+ '%(num)s apple': u'%(num)s Apfel',
+ '%(num)s apples': u'%(num)s Äpfel'
+ }
+}
+
+
+@contextfunction
+def gettext(context, string):
+ language = context.get('LANGUAGE', 'en')
+ return languages.get(language, {}).get(string, string)
+
+
+@contextfunction
+def ngettext(context, s, p, n):
+ language = context.get('LANGUAGE', 'en')
+ if n != 1:
+ return languages.get(language, {}).get(p, p)
+ return languages.get(language, {}).get(s, s)
+
+
+i18n_env = Environment(
+ loader=DictLoader(i18n_templates),
+ extensions=['jinja2.ext.i18n']
+)
+i18n_env.globals.update({
+ '_': gettext,
+ 'gettext': gettext,
+ 'ngettext': ngettext
+})
+
+newstyle_i18n_env = Environment(
+ loader=DictLoader(newstyle_i18n_templates),
+ extensions=['jinja2.ext.i18n']
+)
+newstyle_i18n_env.install_gettext_callables(gettext, ngettext, newstyle=True)
+
+class TestExtension(Extension):
+ tags = set(['test'])
+ ext_attr = 42
+
+ def parse(self, parser):
+ return nodes.Output([self.call_method('_dump', [
+ nodes.EnvironmentAttribute('sandboxed'),
+ self.attr('ext_attr'),
+ nodes.ImportedName(__name__ + '.importable_object'),
+ nodes.ContextReference()
+ ])]).set_lineno(next(parser.stream).lineno)
+
+ def _dump(self, sandboxed, ext_attr, imported_object, context):
+ return '%s|%s|%s|%s' % (
+ sandboxed,
+ ext_attr,
+ imported_object,
+ context.blocks
+ )
+
+
+class PreprocessorExtension(Extension):
+
+ def preprocess(self, source, name, filename=None):
+ return source.replace('[[TEST]]', '({{ foo }})')
+
+
+class StreamFilterExtension(Extension):
+
+ def filter_stream(self, stream):
+ for token in stream:
+ if token.type == 'data':
+ for t in self.interpolate(token):
+ yield t
+ else:
+ yield token
+
+ def interpolate(self, token):
+ pos = 0
+ end = len(token.value)
+ lineno = token.lineno
+ while 1:
+ match = _gettext_re.search(token.value, pos)
+ if match is None:
+ break
+ value = token.value[pos:match.start()]
+ if value:
+ yield Token(lineno, 'data', value)
+ lineno += count_newlines(token.value)
+ yield Token(lineno, 'variable_begin', None)
+ yield Token(lineno, 'name', 'gettext')
+ yield Token(lineno, 'lparen', None)
+ yield Token(lineno, 'string', match.group(1))
+ yield Token(lineno, 'rparen', None)
+ yield Token(lineno, 'variable_end', None)
+ pos = match.end()
+ if pos < end:
+ yield Token(lineno, 'data', token.value[pos:])
+
+
+class ExtensionsTestCase(JinjaTestCase):
+
+ def test_extend_late(self):
+ env = Environment()
+ env.add_extension('jinja2.ext.autoescape')
+ t = env.from_string('{% autoescape true %}{{ "<test>" }}{% endautoescape %}')
+ assert t.render() == '&lt;test&gt;'
+
+ def test_loop_controls(self):
+ env = Environment(extensions=['jinja2.ext.loopcontrols'])
+
+ tmpl = env.from_string('''
+ {%- for item in [1, 2, 3, 4] %}
+ {%- if item % 2 == 0 %}{% continue %}{% endif -%}
+ {{ item }}
+ {%- endfor %}''')
+ assert tmpl.render() == '13'
+
+ tmpl = env.from_string('''
+ {%- for item in [1, 2, 3, 4] %}
+ {%- if item > 2 %}{% break %}{% endif -%}
+ {{ item }}
+ {%- endfor %}''')
+ assert tmpl.render() == '12'
+
+ def test_do(self):
+ env = Environment(extensions=['jinja2.ext.do'])
+ tmpl = env.from_string('''
+ {%- set items = [] %}
+ {%- for char in "foo" %}
+ {%- do items.append(loop.index0 ~ char) %}
+ {%- endfor %}{{ items|join(', ') }}''')
+ assert tmpl.render() == '0f, 1o, 2o'
+
+ def test_with(self):
+ env = Environment(extensions=['jinja2.ext.with_'])
+ tmpl = env.from_string('''\
+ {% with a=42, b=23 -%}
+ {{ a }} = {{ b }}
+ {% endwith -%}
+ {{ a }} = {{ b }}\
+ ''')
+ assert [x.strip() for x in tmpl.render(a=1, b=2).splitlines()] \
+ == ['42 = 23', '1 = 2']
+
+ def test_extension_nodes(self):
+ env = Environment(extensions=[TestExtension])
+ tmpl = env.from_string('{% test %}')
+ assert tmpl.render() == 'False|42|23|{}'
+
+ def test_identifier(self):
+ assert TestExtension.identifier == __name__ + '.TestExtension'
+
+ def test_rebinding(self):
+ original = Environment(extensions=[TestExtension])
+ overlay = original.overlay()
+ for env in original, overlay:
+ for ext in env.extensions.itervalues():
+ assert ext.environment is env
+
+ def test_preprocessor_extension(self):
+ env = Environment(extensions=[PreprocessorExtension])
+ tmpl = env.from_string('{[[TEST]]}')
+ assert tmpl.render(foo=42) == '{(42)}'
+
+ def test_streamfilter_extension(self):
+ env = Environment(extensions=[StreamFilterExtension])
+ env.globals['gettext'] = lambda x: x.upper()
+ tmpl = env.from_string('Foo _(bar) Baz')
+ out = tmpl.render()
+ assert out == 'Foo BAR Baz'
+
+ def test_extension_ordering(self):
+ class T1(Extension):
+ priority = 1
+ class T2(Extension):
+ priority = 2
+ env = Environment(extensions=[T1, T2])
+ ext = list(env.iter_extensions())
+ assert ext[0].__class__ is T1
+ assert ext[1].__class__ is T2
+
+
+class InternationalizationTestCase(JinjaTestCase):
+
+ def test_trans(self):
+ tmpl = i18n_env.get_template('child.html')
+ assert tmpl.render(LANGUAGE='de') == '<title>fehlend</title>pass auf'
+
+ def test_trans_plural(self):
+ tmpl = i18n_env.get_template('plural.html')
+ assert tmpl.render(LANGUAGE='de', user_count=1) == 'Ein Benutzer online'
+ assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online'
+
+ def test_complex_plural(self):
+ tmpl = i18n_env.from_string('{% trans foo=42, count=2 %}{{ count }} item{% '
+ 'pluralize count %}{{ count }} items{% endtrans %}')
+ assert tmpl.render() == '2 items'
+ self.assert_raises(TemplateAssertionError, i18n_env.from_string,
+ '{% trans foo %}...{% pluralize bar %}...{% endtrans %}')
+
+ def test_trans_stringformatting(self):
+ tmpl = i18n_env.get_template('stringformat.html')
+ assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5'
+
+ def test_extract(self):
+ from jinja2.ext import babel_extract
+ source = BytesIO('''
+ {{ gettext('Hello World') }}
+ {% trans %}Hello World{% endtrans %}
+ {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %}
+ '''.encode('ascii')) # make python 3 happy
+ assert list(babel_extract(source, ('gettext', 'ngettext', '_'), [], {})) == [
+ (2, 'gettext', u'Hello World', []),
+ (3, 'gettext', u'Hello World', []),
+ (4, 'ngettext', (u'%(users)s user', u'%(users)s users', None), [])
+ ]
+
+ def test_comment_extract(self):
+ from jinja2.ext import babel_extract
+ source = BytesIO('''
+ {# trans first #}
+ {{ gettext('Hello World') }}
+ {% trans %}Hello World{% endtrans %}{# trans second #}
+ {#: third #}
+ {% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %}
+ '''.encode('utf-8')) # make python 3 happy
+ assert list(babel_extract(source, ('gettext', 'ngettext', '_'), ['trans', ':'], {})) == [
+ (3, 'gettext', u'Hello World', ['first']),
+ (4, 'gettext', u'Hello World', ['second']),
+ (6, 'ngettext', (u'%(users)s user', u'%(users)s users', None), ['third'])
+ ]
+
+
+class NewstyleInternationalizationTestCase(JinjaTestCase):
+
+ def test_trans(self):
+ tmpl = newstyle_i18n_env.get_template('child.html')
+ assert tmpl.render(LANGUAGE='de') == '<title>fehlend</title>pass auf'
+
+ def test_trans_plural(self):
+ tmpl = newstyle_i18n_env.get_template('plural.html')
+ assert tmpl.render(LANGUAGE='de', user_count=1) == 'Ein Benutzer online'
+ assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online'
+
+ def test_complex_plural(self):
+ tmpl = newstyle_i18n_env.from_string('{% trans foo=42, count=2 %}{{ count }} item{% '
+ 'pluralize count %}{{ count }} items{% endtrans %}')
+ assert tmpl.render() == '2 items'
+ self.assert_raises(TemplateAssertionError, i18n_env.from_string,
+ '{% trans foo %}...{% pluralize bar %}...{% endtrans %}')
+
+ def test_trans_stringformatting(self):
+ tmpl = newstyle_i18n_env.get_template('stringformat.html')
+ assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5'
+
+ def test_newstyle_plural(self):
+ tmpl = newstyle_i18n_env.get_template('ngettext.html')
+ assert tmpl.render(LANGUAGE='de', apples=1) == '1 Apfel'
+ assert tmpl.render(LANGUAGE='de', apples=5) == u'5 Äpfel'
+
+ def test_autoescape_support(self):
+ env = Environment(extensions=['jinja2.ext.autoescape',
+ 'jinja2.ext.i18n'])
+ env.install_gettext_callables(lambda x: u'<strong>Wert: %(name)s</strong>',
+ lambda s, p, n: s, newstyle=True)
+ t = env.from_string('{% autoescape ae %}{{ gettext("foo", name='
+ '"<test>") }}{% endautoescape %}')
+ assert t.render(ae=True) == '<strong>Wert: &lt;test&gt;</strong>'
+ assert t.render(ae=False) == '<strong>Wert: <test></strong>'
+
+ def test_num_used_twice(self):
+ tmpl = newstyle_i18n_env.get_template('ngettext_long.html')
+ assert tmpl.render(apples=5, LANGUAGE='de') == u'5 Äpfel'
+
+ def test_num_called_num(self):
+ source = newstyle_i18n_env.compile('''
+ {% trans num=3 %}{{ num }} apple{% pluralize
+ %}{{ num }} apples{% endtrans %}
+ ''', raw=True)
+ # quite hacky, but the only way to properly test that. The idea is
+ # that the generated code does not pass num twice (although that
+ # would work) for better performance. This only works on the
+ # newstyle gettext of course
+ assert re.search(r"l_ngettext, u?'\%\(num\)s apple', u?'\%\(num\)s "
+ r"apples', 3", source) is not None
+
+ def test_trans_vars(self):
+ t1 = newstyle_i18n_env.get_template('transvars1.html')
+ t2 = newstyle_i18n_env.get_template('transvars2.html')
+ t3 = newstyle_i18n_env.get_template('transvars3.html')
+ assert t1.render(num=1, LANGUAGE='de') == 'Benutzer: 1'
+ assert t2.render(count=23, LANGUAGE='de') == 'Benutzer: 23'
+ assert t3.render(num=42, LANGUAGE='de') == 'Benutzer: 42'
+
+ def test_novars_vars_escaping(self):
+ t = newstyle_i18n_env.get_template('novars.html')
+ assert t.render() == '%(hello)s'
+ t = newstyle_i18n_env.get_template('vars.html')
+ assert t.render(foo='42') == '42%(foo)s'
+ t = newstyle_i18n_env.get_template('explicitvars.html')
+ assert t.render() == '%(foo)s'
+
+
+class AutoEscapeTestCase(JinjaTestCase):
+
+ def test_scoped_setting(self):
+ env = Environment(extensions=['jinja2.ext.autoescape'],
+ autoescape=True)
+ tmpl = env.from_string('''
+ {{ "<HelloWorld>" }}
+ {% autoescape false %}
+ {{ "<HelloWorld>" }}
+ {% endautoescape %}
+ {{ "<HelloWorld>" }}
+ ''')
+ assert tmpl.render().split() == \
+ [u'&lt;HelloWorld&gt;', u'<HelloWorld>', u'&lt;HelloWorld&gt;']
+
+ env = Environment(extensions=['jinja2.ext.autoescape'],
+ autoescape=False)
+ tmpl = env.from_string('''
+ {{ "<HelloWorld>" }}
+ {% autoescape true %}
+ {{ "<HelloWorld>" }}
+ {% endautoescape %}
+ {{ "<HelloWorld>" }}
+ ''')
+ assert tmpl.render().split() == \
+ [u'<HelloWorld>', u'&lt;HelloWorld&gt;', u'<HelloWorld>']
+
+ def test_nonvolatile(self):
+ env = Environment(extensions=['jinja2.ext.autoescape'],
+ autoescape=True)
+ tmpl = env.from_string('{{ {"foo": "<test>"}|xmlattr|escape }}')
+ assert tmpl.render() == ' foo="&lt;test&gt;"'
+ tmpl = env.from_string('{% autoescape false %}{{ {"foo": "<test>"}'
+ '|xmlattr|escape }}{% endautoescape %}')
+ assert tmpl.render() == ' foo=&#34;&amp;lt;test&amp;gt;&#34;'
+
+ def test_volatile(self):
+ env = Environment(extensions=['jinja2.ext.autoescape'],
+ autoescape=True)
+ tmpl = env.from_string('{% autoescape foo %}{{ {"foo": "<test>"}'
+ '|xmlattr|escape }}{% endautoescape %}')
+ assert tmpl.render(foo=False) == ' foo=&#34;&amp;lt;test&amp;gt;&#34;'
+ assert tmpl.render(foo=True) == ' foo="&lt;test&gt;"'
+
+ def test_scoping(self):
+ env = Environment(extensions=['jinja2.ext.autoescape'])
+ tmpl = env.from_string('{% autoescape true %}{% set x = "<x>" %}{{ x }}'
+ '{% endautoescape %}{{ x }}{{ "<y>" }}')
+ assert tmpl.render(x=1) == '&lt;x&gt;1<y>'
+
+ def test_volatile_scoping(self):
+ env = Environment(extensions=['jinja2.ext.autoescape'])
+ tmplsource = '''
+ {% autoescape val %}
+ {% macro foo(x) %}
+ [{{ x }}]
+ {% endmacro %}
+ {{ foo().__class__.__name__ }}
+ {% endautoescape %}
+ {{ '<testing>' }}
+ '''
+ tmpl = env.from_string(tmplsource)
+ assert tmpl.render(val=True).split()[0] == 'Markup'
+ assert tmpl.render(val=False).split()[0] == unicode.__name__
+
+ # looking at the source we should see <testing> there in raw
+ # (and then escaped as well)
+ env = Environment(extensions=['jinja2.ext.autoescape'])
+ pysource = env.compile(tmplsource, raw=True)
+ assert '<testing>\\n' in pysource
+
+ env = Environment(extensions=['jinja2.ext.autoescape'],
+ autoescape=True)
+ pysource = env.compile(tmplsource, raw=True)
+ assert '&lt;testing&gt;\\n' in pysource
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ExtensionsTestCase))
+ suite.addTest(unittest.makeSuite(InternationalizationTestCase))
+ suite.addTest(unittest.makeSuite(NewstyleInternationalizationTestCase))
+ suite.addTest(unittest.makeSuite(AutoEscapeTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/filters.py b/websdk/jinja2/testsuite/filters.py
new file mode 100644
index 0000000..aefe768
--- /dev/null
+++ b/websdk/jinja2/testsuite/filters.py
@@ -0,0 +1,356 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.filters
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests for the jinja filters.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Markup, Environment
+
+env = Environment()
+
+
+class FilterTestCase(JinjaTestCase):
+
+ def test_capitalize(self):
+ tmpl = env.from_string('{{ "foo bar"|capitalize }}')
+ assert tmpl.render() == 'Foo bar'
+
+ def test_center(self):
+ tmpl = env.from_string('{{ "foo"|center(9) }}')
+ assert tmpl.render() == ' foo '
+
+ def test_default(self):
+ tmpl = env.from_string(
+ "{{ missing|default('no') }}|{{ false|default('no') }}|"
+ "{{ false|default('no', true) }}|{{ given|default('no') }}"
+ )
+ assert tmpl.render(given='yes') == 'no|False|no|yes'
+
+ def test_dictsort(self):
+ tmpl = env.from_string(
+ '{{ foo|dictsort }}|'
+ '{{ foo|dictsort(true) }}|'
+ '{{ foo|dictsort(false, "value") }}'
+ )
+ out = tmpl.render(foo={"aa": 0, "b": 1, "c": 2, "AB": 3})
+ assert out == ("[('aa', 0), ('AB', 3), ('b', 1), ('c', 2)]|"
+ "[('AB', 3), ('aa', 0), ('b', 1), ('c', 2)]|"
+ "[('aa', 0), ('b', 1), ('c', 2), ('AB', 3)]")
+
+ def test_batch(self):
+ tmpl = env.from_string("{{ foo|batch(3)|list }}|"
+ "{{ foo|batch(3, 'X')|list }}")
+ out = tmpl.render(foo=range(10))
+ assert out == ("[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]|"
+ "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 'X', 'X']]")
+
+ def test_slice(self):
+ tmpl = env.from_string('{{ foo|slice(3)|list }}|'
+ '{{ foo|slice(3, "X")|list }}')
+ out = tmpl.render(foo=range(10))
+ assert out == ("[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|"
+ "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]")
+
+ def test_escape(self):
+ tmpl = env.from_string('''{{ '<">&'|escape }}''')
+ out = tmpl.render()
+ assert out == '&lt;&#34;&gt;&amp;'
+
+ def test_striptags(self):
+ tmpl = env.from_string('''{{ foo|striptags }}''')
+ out = tmpl.render(foo=' <p>just a small \n <a href="#">'
+ 'example</a> link</p>\n<p>to a webpage</p> '
+ '<!-- <p>and some commented stuff</p> -->')
+ assert out == 'just a small example link to a webpage'
+
+ def test_filesizeformat(self):
+ tmpl = env.from_string(
+ '{{ 100|filesizeformat }}|'
+ '{{ 1000|filesizeformat }}|'
+ '{{ 1000000|filesizeformat }}|'
+ '{{ 1000000000|filesizeformat }}|'
+ '{{ 1000000000000|filesizeformat }}|'
+ '{{ 100|filesizeformat(true) }}|'
+ '{{ 1000|filesizeformat(true) }}|'
+ '{{ 1000000|filesizeformat(true) }}|'
+ '{{ 1000000000|filesizeformat(true) }}|'
+ '{{ 1000000000000|filesizeformat(true) }}'
+ )
+ out = tmpl.render()
+ assert out == (
+ '100 Bytes|0.0 kB|0.0 MB|0.0 GB|0.0 TB|100 Bytes|'
+ '1000 Bytes|1.0 KiB|0.9 MiB|0.9 GiB'
+ )
+
+ def test_first(self):
+ tmpl = env.from_string('{{ foo|first }}')
+ out = tmpl.render(foo=range(10))
+ assert out == '0'
+
+ def test_float(self):
+ tmpl = env.from_string('{{ "42"|float }}|'
+ '{{ "ajsghasjgd"|float }}|'
+ '{{ "32.32"|float }}')
+ out = tmpl.render()
+ assert out == '42.0|0.0|32.32'
+
+ def test_format(self):
+ tmpl = env.from_string('''{{ "%s|%s"|format("a", "b") }}''')
+ out = tmpl.render()
+ assert out == 'a|b'
+
+ def test_indent(self):
+ tmpl = env.from_string('{{ foo|indent(2) }}|{{ foo|indent(2, true) }}')
+ text = '\n'.join([' '.join(['foo', 'bar'] * 2)] * 2)
+ out = tmpl.render(foo=text)
+ assert out == ('foo bar foo bar\n foo bar foo bar| '
+ 'foo bar foo bar\n foo bar foo bar')
+
+ def test_int(self):
+ tmpl = env.from_string('{{ "42"|int }}|{{ "ajsghasjgd"|int }}|'
+ '{{ "32.32"|int }}')
+ out = tmpl.render()
+ assert out == '42|0|32'
+
+ def test_join(self):
+ tmpl = env.from_string('{{ [1, 2, 3]|join("|") }}')
+ out = tmpl.render()
+ assert out == '1|2|3'
+
+ env2 = Environment(autoescape=True)
+ tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}')
+ assert tmpl.render() == '&lt;foo&gt;<span>foo</span>'
+
+ def test_join_attribute(self):
+ class User(object):
+ def __init__(self, username):
+ self.username = username
+ tmpl = env.from_string('''{{ users|join(', ', 'username') }}''')
+ assert tmpl.render(users=map(User, ['foo', 'bar'])) == 'foo, bar'
+
+ def test_last(self):
+ tmpl = env.from_string('''{{ foo|last }}''')
+ out = tmpl.render(foo=range(10))
+ assert out == '9'
+
+ def test_length(self):
+ tmpl = env.from_string('''{{ "hello world"|length }}''')
+ out = tmpl.render()
+ assert out == '11'
+
+ def test_lower(self):
+ tmpl = env.from_string('''{{ "FOO"|lower }}''')
+ out = tmpl.render()
+ assert out == 'foo'
+
+ def test_pprint(self):
+ from pprint import pformat
+ tmpl = env.from_string('''{{ data|pprint }}''')
+ data = range(1000)
+ assert tmpl.render(data=data) == pformat(data)
+
+ def test_random(self):
+ tmpl = env.from_string('''{{ seq|random }}''')
+ seq = range(100)
+ for _ in range(10):
+ assert int(tmpl.render(seq=seq)) in seq
+
+ def test_reverse(self):
+ tmpl = env.from_string('{{ "foobar"|reverse|join }}|'
+ '{{ [1, 2, 3]|reverse|list }}')
+ assert tmpl.render() == 'raboof|[3, 2, 1]'
+
+ def test_string(self):
+ x = [1, 2, 3, 4, 5]
+ tmpl = env.from_string('''{{ obj|string }}''')
+ assert tmpl.render(obj=x) == unicode(x)
+
+ def test_title(self):
+ tmpl = env.from_string('''{{ "foo bar"|title }}''')
+ assert tmpl.render() == "Foo Bar"
+
+ def test_truncate(self):
+ tmpl = env.from_string(
+ '{{ data|truncate(15, true, ">>>") }}|'
+ '{{ data|truncate(15, false, ">>>") }}|'
+ '{{ smalldata|truncate(15) }}'
+ )
+ out = tmpl.render(data='foobar baz bar' * 1000,
+ smalldata='foobar baz bar')
+ assert out == 'foobar baz barf>>>|foobar baz >>>|foobar baz bar'
+
+ def test_upper(self):
+ tmpl = env.from_string('{{ "foo"|upper }}')
+ assert tmpl.render() == 'FOO'
+
+ def test_urlize(self):
+ tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}')
+ assert tmpl.render() == 'foo <a href="http://www.example.com/">'\
+ 'http://www.example.com/</a> bar'
+
+ def test_wordcount(self):
+ tmpl = env.from_string('{{ "foo bar baz"|wordcount }}')
+ assert tmpl.render() == '3'
+
+ def test_block(self):
+ tmpl = env.from_string('{% filter lower|escape %}<HEHE>{% endfilter %}')
+ assert tmpl.render() == '&lt;hehe&gt;'
+
+ def test_chaining(self):
+ tmpl = env.from_string('''{{ ['<foo>', '<bar>']|first|upper|escape }}''')
+ assert tmpl.render() == '&lt;FOO&gt;'
+
+ def test_sum(self):
+ tmpl = env.from_string('''{{ [1, 2, 3, 4, 5, 6]|sum }}''')
+ assert tmpl.render() == '21'
+
+ def test_sum_attributes(self):
+ tmpl = env.from_string('''{{ values|sum('value') }}''')
+ assert tmpl.render(values=[
+ {'value': 23},
+ {'value': 1},
+ {'value': 18},
+ ]) == '42'
+
+ def test_sum_attributes_nested(self):
+ tmpl = env.from_string('''{{ values|sum('real.value') }}''')
+ assert tmpl.render(values=[
+ {'real': {'value': 23}},
+ {'real': {'value': 1}},
+ {'real': {'value': 18}},
+ ]) == '42'
+
+ def test_abs(self):
+ tmpl = env.from_string('''{{ -1|abs }}|{{ 1|abs }}''')
+ assert tmpl.render() == '1|1', tmpl.render()
+
+ def test_round_positive(self):
+ tmpl = env.from_string('{{ 2.7|round }}|{{ 2.1|round }}|'
+ "{{ 2.1234|round(3, 'floor') }}|"
+ "{{ 2.1|round(0, 'ceil') }}")
+ assert tmpl.render() == '3.0|2.0|2.123|3.0', tmpl.render()
+
+ def test_round_negative(self):
+ tmpl = env.from_string('{{ 21.3|round(-1)}}|'
+ "{{ 21.3|round(-1, 'ceil')}}|"
+ "{{ 21.3|round(-1, 'floor')}}")
+ assert tmpl.render() == '20.0|30.0|20.0',tmpl.render()
+
+ def test_xmlattr(self):
+ tmpl = env.from_string("{{ {'foo': 42, 'bar': 23, 'fish': none, "
+ "'spam': missing, 'blub:blub': '<?>'}|xmlattr }}")
+ out = tmpl.render().split()
+ assert len(out) == 3
+ assert 'foo="42"' in out
+ assert 'bar="23"' in out
+ assert 'blub:blub="&lt;?&gt;"' in out
+
+ def test_sort1(self):
+ tmpl = env.from_string('{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}')
+ assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
+
+ def test_sort2(self):
+ tmpl = env.from_string('{{ "".join(["c", "A", "b", "D"]|sort) }}')
+ assert tmpl.render() == 'AbcD'
+
+ def test_sort3(self):
+ tmpl = env.from_string('''{{ ['foo', 'Bar', 'blah']|sort }}''')
+ assert tmpl.render() == "['Bar', 'blah', 'foo']"
+
+ def test_sort4(self):
+ class Magic(object):
+ def __init__(self, value):
+ self.value = value
+ def __unicode__(self):
+ return unicode(self.value)
+ tmpl = env.from_string('''{{ items|sort(attribute='value')|join }}''')
+ assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == '1234'
+
+ def test_groupby(self):
+ tmpl = env.from_string('''
+ {%- for grouper, list in [{'foo': 1, 'bar': 2},
+ {'foo': 2, 'bar': 3},
+ {'foo': 1, 'bar': 1},
+ {'foo': 3, 'bar': 4}]|groupby('foo') -%}
+ {{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}|
+ {%- endfor %}''')
+ assert tmpl.render().split('|') == [
+ "1: 1, 2: 1, 1",
+ "2: 2, 3",
+ "3: 3, 4",
+ ""
+ ]
+
+ def test_groupby_tuple_index(self):
+ tmpl = env.from_string('''
+ {%- for grouper, list in [('a', 1), ('a', 2), ('b', 1)]|groupby(0) -%}
+ {{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}|
+ {%- endfor %}''')
+ assert tmpl.render() == 'a:1:2|b:1|'
+
+ def test_groupby_multidot(self):
+ class Date(object):
+ def __init__(self, day, month, year):
+ self.day = day
+ self.month = month
+ self.year = year
+ class Article(object):
+ def __init__(self, title, *date):
+ self.date = Date(*date)
+ self.title = title
+ articles = [
+ Article('aha', 1, 1, 1970),
+ Article('interesting', 2, 1, 1970),
+ Article('really?', 3, 1, 1970),
+ Article('totally not', 1, 1, 1971)
+ ]
+ tmpl = env.from_string('''
+ {%- for year, list in articles|groupby('date.year') -%}
+ {{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}|
+ {%- endfor %}''')
+ assert tmpl.render(articles=articles).split('|') == [
+ '1970[aha][interesting][really?]',
+ '1971[totally not]',
+ ''
+ ]
+
+ def test_filtertag(self):
+ tmpl = env.from_string("{% filter upper|replace('FOO', 'foo') %}"
+ "foobar{% endfilter %}")
+ assert tmpl.render() == 'fooBAR'
+
+ def test_replace(self):
+ env = Environment()
+ tmpl = env.from_string('{{ string|replace("o", 42) }}')
+ assert tmpl.render(string='<foo>') == '<f4242>'
+ env = Environment(autoescape=True)
+ tmpl = env.from_string('{{ string|replace("o", 42) }}')
+ assert tmpl.render(string='<foo>') == '&lt;f4242&gt;'
+ tmpl = env.from_string('{{ string|replace("<", 42) }}')
+ assert tmpl.render(string='<foo>') == '42foo&gt;'
+ tmpl = env.from_string('{{ string|replace("o", ">x<") }}')
+ assert tmpl.render(string=Markup('foo')) == 'f&gt;x&lt;&gt;x&lt;'
+
+ def test_forceescape(self):
+ tmpl = env.from_string('{{ x|forceescape }}')
+ assert tmpl.render(x=Markup('<div />')) == u'&lt;div /&gt;'
+
+ def test_safe(self):
+ env = Environment(autoescape=True)
+ tmpl = env.from_string('{{ "<div>foo</div>"|safe }}')
+ assert tmpl.render() == '<div>foo</div>'
+ tmpl = env.from_string('{{ "<div>foo</div>" }}')
+ assert tmpl.render() == '&lt;div&gt;foo&lt;/div&gt;'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(FilterTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/imports.py b/websdk/jinja2/testsuite/imports.py
new file mode 100644
index 0000000..1cb12cb
--- /dev/null
+++ b/websdk/jinja2/testsuite/imports.py
@@ -0,0 +1,141 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.imports
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the import features (with includes).
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Environment, DictLoader
+from jinja2.exceptions import TemplateNotFound, TemplatesNotFound
+
+
+test_env = Environment(loader=DictLoader(dict(
+ module='{% macro test() %}[{{ foo }}|{{ bar }}]{% endmacro %}',
+ header='[{{ foo }}|{{ 23 }}]',
+ o_printer='({{ o }})'
+)))
+test_env.globals['bar'] = 23
+
+
+class ImportsTestCase(JinjaTestCase):
+
+ def test_context_imports(self):
+ t = test_env.from_string('{% import "module" as m %}{{ m.test() }}')
+ assert t.render(foo=42) == '[|23]'
+ t = test_env.from_string('{% import "module" as m without context %}{{ m.test() }}')
+ assert t.render(foo=42) == '[|23]'
+ t = test_env.from_string('{% import "module" as m with context %}{{ m.test() }}')
+ assert t.render(foo=42) == '[42|23]'
+ t = test_env.from_string('{% from "module" import test %}{{ test() }}')
+ assert t.render(foo=42) == '[|23]'
+ t = test_env.from_string('{% from "module" import test without context %}{{ test() }}')
+ assert t.render(foo=42) == '[|23]'
+ t = test_env.from_string('{% from "module" import test with context %}{{ test() }}')
+ assert t.render(foo=42) == '[42|23]'
+
+ def test_trailing_comma(self):
+ test_env.from_string('{% from "foo" import bar, baz with context %}')
+ test_env.from_string('{% from "foo" import bar, baz, with context %}')
+ test_env.from_string('{% from "foo" import bar, with context %}')
+ test_env.from_string('{% from "foo" import bar, with, context %}')
+ test_env.from_string('{% from "foo" import bar, with with context %}')
+
+ def test_exports(self):
+ m = test_env.from_string('''
+ {% macro toplevel() %}...{% endmacro %}
+ {% macro __private() %}...{% endmacro %}
+ {% set variable = 42 %}
+ {% for item in [1] %}
+ {% macro notthere() %}{% endmacro %}
+ {% endfor %}
+ ''').module
+ assert m.toplevel() == '...'
+ assert not hasattr(m, '__missing')
+ assert m.variable == 42
+ assert not hasattr(m, 'notthere')
+
+
+class IncludesTestCase(JinjaTestCase):
+
+ def test_context_include(self):
+ t = test_env.from_string('{% include "header" %}')
+ assert t.render(foo=42) == '[42|23]'
+ t = test_env.from_string('{% include "header" with context %}')
+ assert t.render(foo=42) == '[42|23]'
+ t = test_env.from_string('{% include "header" without context %}')
+ assert t.render(foo=42) == '[|23]'
+
+ def test_choice_includes(self):
+ t = test_env.from_string('{% include ["missing", "header"] %}')
+ assert t.render(foo=42) == '[42|23]'
+
+ t = test_env.from_string('{% include ["missing", "missing2"] ignore missing %}')
+ assert t.render(foo=42) == ''
+
+ t = test_env.from_string('{% include ["missing", "missing2"] %}')
+ self.assert_raises(TemplateNotFound, t.render)
+ try:
+ t.render()
+ except TemplatesNotFound, e:
+ assert e.templates == ['missing', 'missing2']
+ assert e.name == 'missing2'
+ else:
+ assert False, 'thou shalt raise'
+
+ def test_includes(t, **ctx):
+ ctx['foo'] = 42
+ assert t.render(ctx) == '[42|23]'
+
+ t = test_env.from_string('{% include ["missing", "header"] %}')
+ test_includes(t)
+ t = test_env.from_string('{% include x %}')
+ test_includes(t, x=['missing', 'header'])
+ t = test_env.from_string('{% include [x, "header"] %}')
+ test_includes(t, x='missing')
+ t = test_env.from_string('{% include x %}')
+ test_includes(t, x='header')
+ t = test_env.from_string('{% include x %}')
+ test_includes(t, x='header')
+ t = test_env.from_string('{% include [x] %}')
+ test_includes(t, x='header')
+
+ def test_include_ignoring_missing(self):
+ t = test_env.from_string('{% include "missing" %}')
+ self.assert_raises(TemplateNotFound, t.render)
+ for extra in '', 'with context', 'without context':
+ t = test_env.from_string('{% include "missing" ignore missing ' +
+ extra + ' %}')
+ assert t.render() == ''
+
+ def test_context_include_with_overrides(self):
+ env = Environment(loader=DictLoader(dict(
+ main="{% for item in [1, 2, 3] %}{% include 'item' %}{% endfor %}",
+ item="{{ item }}"
+ )))
+ assert env.get_template("main").render() == "123"
+
+ def test_unoptimized_scopes(self):
+ t = test_env.from_string("""
+ {% macro outer(o) %}
+ {% macro inner() %}
+ {% include "o_printer" %}
+ {% endmacro %}
+ {{ inner() }}
+ {% endmacro %}
+ {{ outer("FOO") }}
+ """)
+ assert t.render().strip() == '(FOO)'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ImportsTestCase))
+ suite.addTest(unittest.makeSuite(IncludesTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/inheritance.py b/websdk/jinja2/testsuite/inheritance.py
new file mode 100644
index 0000000..355aa0c
--- /dev/null
+++ b/websdk/jinja2/testsuite/inheritance.py
@@ -0,0 +1,227 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.inheritance
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the template inheritance feature.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Environment, DictLoader
+
+
+LAYOUTTEMPLATE = '''\
+|{% block block1 %}block 1 from layout{% endblock %}
+|{% block block2 %}block 2 from layout{% endblock %}
+|{% block block3 %}
+{% block block4 %}nested block 4 from layout{% endblock %}
+{% endblock %}|'''
+
+LEVEL1TEMPLATE = '''\
+{% extends "layout" %}
+{% block block1 %}block 1 from level1{% endblock %}'''
+
+LEVEL2TEMPLATE = '''\
+{% extends "level1" %}
+{% block block2 %}{% block block5 %}nested block 5 from level2{%
+endblock %}{% endblock %}'''
+
+LEVEL3TEMPLATE = '''\
+{% extends "level2" %}
+{% block block5 %}block 5 from level3{% endblock %}
+{% block block4 %}block 4 from level3{% endblock %}
+'''
+
+LEVEL4TEMPLATE = '''\
+{% extends "level3" %}
+{% block block3 %}block 3 from level4{% endblock %}
+'''
+
+WORKINGTEMPLATE = '''\
+{% extends "layout" %}
+{% block block1 %}
+ {% if false %}
+ {% block block2 %}
+ this should workd
+ {% endblock %}
+ {% endif %}
+{% endblock %}
+'''
+
+env = Environment(loader=DictLoader({
+ 'layout': LAYOUTTEMPLATE,
+ 'level1': LEVEL1TEMPLATE,
+ 'level2': LEVEL2TEMPLATE,
+ 'level3': LEVEL3TEMPLATE,
+ 'level4': LEVEL4TEMPLATE,
+ 'working': WORKINGTEMPLATE
+}), trim_blocks=True)
+
+
+class InheritanceTestCase(JinjaTestCase):
+
+ def test_layout(self):
+ tmpl = env.get_template('layout')
+ assert tmpl.render() == ('|block 1 from layout|block 2 from '
+ 'layout|nested block 4 from layout|')
+
+ def test_level1(self):
+ tmpl = env.get_template('level1')
+ assert tmpl.render() == ('|block 1 from level1|block 2 from '
+ 'layout|nested block 4 from layout|')
+
+ def test_level2(self):
+ tmpl = env.get_template('level2')
+ assert tmpl.render() == ('|block 1 from level1|nested block 5 from '
+ 'level2|nested block 4 from layout|')
+
+ def test_level3(self):
+ tmpl = env.get_template('level3')
+ assert tmpl.render() == ('|block 1 from level1|block 5 from level3|'
+ 'block 4 from level3|')
+
+ def test_level4(sel):
+ tmpl = env.get_template('level4')
+ assert tmpl.render() == ('|block 1 from level1|block 5 from '
+ 'level3|block 3 from level4|')
+
+ def test_super(self):
+ env = Environment(loader=DictLoader({
+ 'a': '{% block intro %}INTRO{% endblock %}|'
+ 'BEFORE|{% block data %}INNER{% endblock %}|AFTER',
+ 'b': '{% extends "a" %}{% block data %}({{ '
+ 'super() }}){% endblock %}',
+ 'c': '{% extends "b" %}{% block intro %}--{{ '
+ 'super() }}--{% endblock %}\n{% block data '
+ '%}[{{ super() }}]{% endblock %}'
+ }))
+ tmpl = env.get_template('c')
+ assert tmpl.render() == '--INTRO--|BEFORE|[(INNER)]|AFTER'
+
+ def test_working(self):
+ tmpl = env.get_template('working')
+
+ def test_reuse_blocks(self):
+ tmpl = env.from_string('{{ self.foo() }}|{% block foo %}42'
+ '{% endblock %}|{{ self.foo() }}')
+ assert tmpl.render() == '42|42|42'
+
+ def test_preserve_blocks(self):
+ env = Environment(loader=DictLoader({
+ 'a': '{% if false %}{% block x %}A{% endblock %}{% endif %}{{ self.x() }}',
+ 'b': '{% extends "a" %}{% block x %}B{{ super() }}{% endblock %}'
+ }))
+ tmpl = env.get_template('b')
+ assert tmpl.render() == 'BA'
+
+ def test_dynamic_inheritance(self):
+ env = Environment(loader=DictLoader({
+ 'master1': 'MASTER1{% block x %}{% endblock %}',
+ 'master2': 'MASTER2{% block x %}{% endblock %}',
+ 'child': '{% extends master %}{% block x %}CHILD{% endblock %}'
+ }))
+ tmpl = env.get_template('child')
+ for m in range(1, 3):
+ assert tmpl.render(master='master%d' % m) == 'MASTER%dCHILD' % m
+
+ def test_multi_inheritance(self):
+ env = Environment(loader=DictLoader({
+ 'master1': 'MASTER1{% block x %}{% endblock %}',
+ 'master2': 'MASTER2{% block x %}{% endblock %}',
+ 'child': '''{% if master %}{% extends master %}{% else %}{% extends
+ 'master1' %}{% endif %}{% block x %}CHILD{% endblock %}'''
+ }))
+ tmpl = env.get_template('child')
+ assert tmpl.render(master='master2') == 'MASTER2CHILD'
+ assert tmpl.render(master='master1') == 'MASTER1CHILD'
+ assert tmpl.render() == 'MASTER1CHILD'
+
+ def test_scoped_block(self):
+ env = Environment(loader=DictLoader({
+ 'master.html': '{% for item in seq %}[{% block item scoped %}'
+ '{% endblock %}]{% endfor %}'
+ }))
+ t = env.from_string('{% extends "master.html" %}{% block item %}'
+ '{{ item }}{% endblock %}')
+ assert t.render(seq=range(5)) == '[0][1][2][3][4]'
+
+ def test_super_in_scoped_block(self):
+ env = Environment(loader=DictLoader({
+ 'master.html': '{% for item in seq %}[{% block item scoped %}'
+ '{{ item }}{% endblock %}]{% endfor %}'
+ }))
+ t = env.from_string('{% extends "master.html" %}{% block item %}'
+ '{{ super() }}|{{ item * 2 }}{% endblock %}')
+ assert t.render(seq=range(5)) == '[0|0][1|2][2|4][3|6][4|8]'
+
+ def test_scoped_block_after_inheritance(self):
+ env = Environment(loader=DictLoader({
+ 'layout.html': '''
+ {% block useless %}{% endblock %}
+ ''',
+ 'index.html': '''
+ {%- extends 'layout.html' %}
+ {% from 'helpers.html' import foo with context %}
+ {% block useless %}
+ {% for x in [1, 2, 3] %}
+ {% block testing scoped %}
+ {{ foo(x) }}
+ {% endblock %}
+ {% endfor %}
+ {% endblock %}
+ ''',
+ 'helpers.html': '''
+ {% macro foo(x) %}{{ the_foo + x }}{% endmacro %}
+ '''
+ }))
+ rv = env.get_template('index.html').render(the_foo=42).split()
+ assert rv == ['43', '44', '45']
+
+
+class BugFixTestCase(JinjaTestCase):
+
+ def test_fixed_macro_scoping_bug(self):
+ assert Environment(loader=DictLoader({
+ 'test.html': '''\
+ {% extends 'details.html' %}
+
+ {% macro my_macro() %}
+ my_macro
+ {% endmacro %}
+
+ {% block inner_box %}
+ {{ my_macro() }}
+ {% endblock %}
+ ''',
+ 'details.html': '''\
+ {% extends 'standard.html' %}
+
+ {% macro my_macro() %}
+ my_macro
+ {% endmacro %}
+
+ {% block content %}
+ {% block outer_box %}
+ outer_box
+ {% block inner_box %}
+ inner_box
+ {% endblock %}
+ {% endblock %}
+ {% endblock %}
+ ''',
+ 'standard.html': '''
+ {% block content %}&nbsp;{% endblock %}
+ '''
+ })).get_template("test.html").render().split() == [u'outer_box', u'my_macro']
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(InheritanceTestCase))
+ suite.addTest(unittest.makeSuite(BugFixTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/lexnparse.py b/websdk/jinja2/testsuite/lexnparse.py
new file mode 100644
index 0000000..77b76ec
--- /dev/null
+++ b/websdk/jinja2/testsuite/lexnparse.py
@@ -0,0 +1,387 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.lexnparse
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ All the unittests regarding lexing, parsing and syntax.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+import unittest
+
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Environment, Template, TemplateSyntaxError, \
+ UndefinedError, nodes
+
+env = Environment()
+
+
+# how does a string look like in jinja syntax?
+if sys.version_info < (3, 0):
+ def jinja_string_repr(string):
+ return repr(string)[1:]
+else:
+ jinja_string_repr = repr
+
+
+class LexerTestCase(JinjaTestCase):
+
+ def test_raw1(self):
+ tmpl = env.from_string('{% raw %}foo{% endraw %}|'
+ '{%raw%}{{ bar }}|{% baz %}{% endraw %}')
+ assert tmpl.render() == 'foo|{{ bar }}|{% baz %}'
+
+ def test_raw2(self):
+ tmpl = env.from_string('1 {%- raw -%} 2 {%- endraw -%} 3')
+ assert tmpl.render() == '123'
+
+ def test_balancing(self):
+ env = Environment('{%', '%}', '${', '}')
+ tmpl = env.from_string('''{% for item in seq
+ %}${{'foo': item}|upper}{% endfor %}''')
+ assert tmpl.render(seq=range(3)) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}"
+
+ def test_comments(self):
+ env = Environment('<!--', '-->', '{', '}')
+ tmpl = env.from_string('''\
+<ul>
+<!--- for item in seq -->
+ <li>{item}</li>
+<!--- endfor -->
+</ul>''')
+ assert tmpl.render(seq=range(3)) == ("<ul>\n <li>0</li>\n "
+ "<li>1</li>\n <li>2</li>\n</ul>")
+
+ def test_string_escapes(self):
+ for char in u'\0', u'\u2668', u'\xe4', u'\t', u'\r', u'\n':
+ tmpl = env.from_string('{{ %s }}' % jinja_string_repr(char))
+ assert tmpl.render() == char
+ assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == u'\u2668'
+
+ def test_bytefallback(self):
+ from pprint import pformat
+ tmpl = env.from_string(u'''{{ 'foo'|pprint }}|{{ 'bär'|pprint }}''')
+ assert tmpl.render() == pformat('foo') + '|' + pformat(u'bär')
+
+ def test_operators(self):
+ from jinja2.lexer import operators
+ for test, expect in operators.iteritems():
+ if test in '([{}])':
+ continue
+ stream = env.lexer.tokenize('{{ %s }}' % test)
+ stream.next()
+ assert stream.current.type == expect
+
+ def test_normalizing(self):
+ for seq in '\r', '\r\n', '\n':
+ env = Environment(newline_sequence=seq)
+ tmpl = env.from_string('1\n2\r\n3\n4\n')
+ result = tmpl.render()
+ assert result.replace(seq, 'X') == '1X2X3X4'
+
+
+class ParserTestCase(JinjaTestCase):
+
+ def test_php_syntax(self):
+ env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->')
+ tmpl = env.from_string('''\
+<!-- I'm a comment, I'm not interesting -->\
+<? for item in seq -?>
+ <?= item ?>
+<?- endfor ?>''')
+ assert tmpl.render(seq=range(5)) == '01234'
+
+ def test_erb_syntax(self):
+ env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>')
+ tmpl = env.from_string('''\
+<%# I'm a comment, I'm not interesting %>\
+<% for item in seq -%>
+ <%= item %>
+<%- endfor %>''')
+ assert tmpl.render(seq=range(5)) == '01234'
+
+ def test_comment_syntax(self):
+ env = Environment('<!--', '-->', '${', '}', '<!--#', '-->')
+ tmpl = env.from_string('''\
+<!--# I'm a comment, I'm not interesting -->\
+<!-- for item in seq --->
+ ${item}
+<!--- endfor -->''')
+ assert tmpl.render(seq=range(5)) == '01234'
+
+ def test_balancing(self):
+ tmpl = env.from_string('''{{{'foo':'bar'}.foo}}''')
+ assert tmpl.render() == 'bar'
+
+ def test_start_comment(self):
+ tmpl = env.from_string('''{# foo comment
+and bar comment #}
+{% macro blub() %}foo{% endmacro %}
+{{ blub() }}''')
+ assert tmpl.render().strip() == 'foo'
+
+ def test_line_syntax(self):
+ env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%')
+ tmpl = env.from_string('''\
+<%# regular comment %>
+% for item in seq:
+ ${item}
+% endfor''')
+ assert [int(x.strip()) for x in tmpl.render(seq=range(5)).split()] == \
+ range(5)
+
+ env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##')
+ tmpl = env.from_string('''\
+<%# regular comment %>
+% for item in seq:
+ ${item} ## the rest of the stuff
+% endfor''')
+ assert [int(x.strip()) for x in tmpl.render(seq=range(5)).split()] == \
+ range(5)
+
+ def test_line_syntax_priority(self):
+ # XXX: why is the whitespace there in front of the newline?
+ env = Environment('{%', '%}', '${', '}', '/*', '*/', '##', '#')
+ tmpl = env.from_string('''\
+/* ignore me.
+ I'm a multiline comment */
+## for item in seq:
+* ${item} # this is just extra stuff
+## endfor''')
+ assert tmpl.render(seq=[1, 2]).strip() == '* 1\n* 2'
+ env = Environment('{%', '%}', '${', '}', '/*', '*/', '#', '##')
+ tmpl = env.from_string('''\
+/* ignore me.
+ I'm a multiline comment */
+# for item in seq:
+* ${item} ## this is just extra stuff
+ ## extra stuff i just want to ignore
+# endfor''')
+ assert tmpl.render(seq=[1, 2]).strip() == '* 1\n\n* 2'
+
+ def test_error_messages(self):
+ def assert_error(code, expected):
+ try:
+ Template(code)
+ except TemplateSyntaxError, e:
+ assert str(e) == expected, 'unexpected error message'
+ else:
+ assert False, 'that was supposed to be an error'
+
+ assert_error('{% for item in seq %}...{% endif %}',
+ "Encountered unknown tag 'endif'. Jinja was looking "
+ "for the following tags: 'endfor' or 'else'. The "
+ "innermost block that needs to be closed is 'for'.")
+ assert_error('{% if foo %}{% for item in seq %}...{% endfor %}{% endfor %}',
+ "Encountered unknown tag 'endfor'. Jinja was looking for "
+ "the following tags: 'elif' or 'else' or 'endif'. The "
+ "innermost block that needs to be closed is 'if'.")
+ assert_error('{% if foo %}',
+ "Unexpected end of template. Jinja was looking for the "
+ "following tags: 'elif' or 'else' or 'endif'. The "
+ "innermost block that needs to be closed is 'if'.")
+ assert_error('{% for item in seq %}',
+ "Unexpected end of template. Jinja was looking for the "
+ "following tags: 'endfor' or 'else'. The innermost block "
+ "that needs to be closed is 'for'.")
+ assert_error('{% block foo-bar-baz %}',
+ "Block names in Jinja have to be valid Python identifiers "
+ "and may not contain hyphens, use an underscore instead.")
+ assert_error('{% unknown_tag %}',
+ "Encountered unknown tag 'unknown_tag'.")
+
+
+class SyntaxTestCase(JinjaTestCase):
+
+ def test_call(self):
+ env = Environment()
+ env.globals['foo'] = lambda a, b, c, e, g: a + b + c + e + g
+ tmpl = env.from_string("{{ foo('a', c='d', e='f', *['b'], **{'g': 'h'}) }}")
+ assert tmpl.render() == 'abdfh'
+
+ def test_slicing(self):
+ tmpl = env.from_string('{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}')
+ assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
+
+ def test_attr(self):
+ tmpl = env.from_string("{{ foo.bar }}|{{ foo['bar'] }}")
+ assert tmpl.render(foo={'bar': 42}) == '42|42'
+
+ def test_subscript(self):
+ tmpl = env.from_string("{{ foo[0] }}|{{ foo[-1] }}")
+ assert tmpl.render(foo=[0, 1, 2]) == '0|2'
+
+ def test_tuple(self):
+ tmpl = env.from_string('{{ () }}|{{ (1,) }}|{{ (1, 2) }}')
+ assert tmpl.render() == '()|(1,)|(1, 2)'
+
+ def test_math(self):
+ tmpl = env.from_string('{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}')
+ assert tmpl.render() == '1.5|8'
+
+ def test_div(self):
+ tmpl = env.from_string('{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}')
+ assert tmpl.render() == '1|1.5|1'
+
+ def test_unary(self):
+ tmpl = env.from_string('{{ +3 }}|{{ -3 }}')
+ assert tmpl.render() == '3|-3'
+
+ def test_concat(self):
+ tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}")
+ assert tmpl.render() == '[1, 2]foo'
+
+ def test_compare(self):
+ tmpl = env.from_string('{{ 1 > 0 }}|{{ 1 >= 1 }}|{{ 2 < 3 }}|'
+ '{{ 2 == 2 }}|{{ 1 <= 1 }}')
+ assert tmpl.render() == 'True|True|True|True|True'
+
+ def test_inop(self):
+ tmpl = env.from_string('{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}')
+ assert tmpl.render() == 'True|False'
+
+ def test_literals(self):
+ tmpl = env.from_string('{{ [] }}|{{ {} }}|{{ () }}')
+ assert tmpl.render().lower() == '[]|{}|()'
+
+ def test_bool(self):
+ tmpl = env.from_string('{{ true and false }}|{{ false '
+ 'or true }}|{{ not false }}')
+ assert tmpl.render() == 'False|True|True'
+
+ def test_grouping(self):
+ tmpl = env.from_string('{{ (true and false) or (false and true) and not false }}')
+ assert tmpl.render() == 'False'
+
+ def test_django_attr(self):
+ tmpl = env.from_string('{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}')
+ assert tmpl.render() == '1|1'
+
+ def test_conditional_expression(self):
+ tmpl = env.from_string('''{{ 0 if true else 1 }}''')
+ assert tmpl.render() == '0'
+
+ def test_short_conditional_expression(self):
+ tmpl = env.from_string('<{{ 1 if false }}>')
+ assert tmpl.render() == '<>'
+
+ tmpl = env.from_string('<{{ (1 if false).bar }}>')
+ self.assert_raises(UndefinedError, tmpl.render)
+
+ def test_filter_priority(self):
+ tmpl = env.from_string('{{ "foo"|upper + "bar"|upper }}')
+ assert tmpl.render() == 'FOOBAR'
+
+ def test_function_calls(self):
+ tests = [
+ (True, '*foo, bar'),
+ (True, '*foo, *bar'),
+ (True, '*foo, bar=42'),
+ (True, '**foo, *bar'),
+ (True, '**foo, bar'),
+ (False, 'foo, bar'),
+ (False, 'foo, bar=42'),
+ (False, 'foo, bar=23, *args'),
+ (False, 'a, b=c, *d, **e'),
+ (False, '*foo, **bar')
+ ]
+ for should_fail, sig in tests:
+ if should_fail:
+ self.assert_raises(TemplateSyntaxError,
+ env.from_string, '{{ foo(%s) }}' % sig)
+ else:
+ env.from_string('foo(%s)' % sig)
+
+ def test_tuple_expr(self):
+ for tmpl in [
+ '{{ () }}',
+ '{{ (1, 2) }}',
+ '{{ (1, 2,) }}',
+ '{{ 1, }}',
+ '{{ 1, 2 }}',
+ '{% for foo, bar in seq %}...{% endfor %}',
+ '{% for x in foo, bar %}...{% endfor %}',
+ '{% for x in foo, %}...{% endfor %}'
+ ]:
+ assert env.from_string(tmpl)
+
+ def test_trailing_comma(self):
+ tmpl = env.from_string('{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}')
+ assert tmpl.render().lower() == '(1, 2)|[1, 2]|{1: 2}'
+
+ def test_block_end_name(self):
+ env.from_string('{% block foo %}...{% endblock foo %}')
+ self.assert_raises(TemplateSyntaxError, env.from_string,
+ '{% block x %}{% endblock y %}')
+
+ def test_constant_casing(self):
+ for const in True, False, None:
+ tmpl = env.from_string('{{ %s }}|{{ %s }}|{{ %s }}' % (
+ str(const), str(const).lower(), str(const).upper()
+ ))
+ assert tmpl.render() == '%s|%s|' % (const, const)
+
+ def test_test_chaining(self):
+ self.assert_raises(TemplateSyntaxError, env.from_string,
+ '{{ foo is string is sequence }}')
+ assert env.from_string('{{ 42 is string or 42 is number }}'
+ ).render() == 'True'
+
+ def test_string_concatenation(self):
+ tmpl = env.from_string('{{ "foo" "bar" "baz" }}')
+ assert tmpl.render() == 'foobarbaz'
+
+ def test_notin(self):
+ bar = xrange(100)
+ tmpl = env.from_string('''{{ not 42 in bar }}''')
+ assert tmpl.render(bar=bar) == unicode(not 42 in bar)
+
+ def test_implicit_subscribed_tuple(self):
+ class Foo(object):
+ def __getitem__(self, x):
+ return x
+ t = env.from_string('{{ foo[1, 2] }}')
+ assert t.render(foo=Foo()) == u'(1, 2)'
+
+ def test_raw2(self):
+ tmpl = env.from_string('{% raw %}{{ FOO }} and {% BAR %}{% endraw %}')
+ assert tmpl.render() == '{{ FOO }} and {% BAR %}'
+
+ def test_const(self):
+ tmpl = env.from_string('{{ true }}|{{ false }}|{{ none }}|'
+ '{{ none is defined }}|{{ missing is defined }}')
+ assert tmpl.render() == 'True|False|None|True|False'
+
+ def test_neg_filter_priority(self):
+ node = env.parse('{{ -1|foo }}')
+ assert isinstance(node.body[0].nodes[0], nodes.Filter)
+ assert isinstance(node.body[0].nodes[0].node, nodes.Neg)
+
+ def test_const_assign(self):
+ constass1 = '''{% set true = 42 %}'''
+ constass2 = '''{% for none in seq %}{% endfor %}'''
+ for tmpl in constass1, constass2:
+ self.assert_raises(TemplateSyntaxError, env.from_string, tmpl)
+
+ def test_localset(self):
+ tmpl = env.from_string('''{% set foo = 0 %}\
+{% for item in [1, 2] %}{% set foo = 1 %}{% endfor %}\
+{{ foo }}''')
+ assert tmpl.render() == '0'
+
+ def test_parse_unary(self):
+ tmpl = env.from_string('{{ -foo["bar"] }}')
+ assert tmpl.render(foo={'bar': 42}) == '-42'
+ tmpl = env.from_string('{{ -foo["bar"]|abs }}')
+ assert tmpl.render(foo={'bar': 42}) == '42'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(LexerTestCase))
+ suite.addTest(unittest.makeSuite(ParserTestCase))
+ suite.addTest(unittest.makeSuite(SyntaxTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/loader.py b/websdk/jinja2/testsuite/loader.py
new file mode 100644
index 0000000..f62ec92
--- /dev/null
+++ b/websdk/jinja2/testsuite/loader.py
@@ -0,0 +1,218 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.loader
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Test the loaders.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+import tempfile
+import shutil
+import unittest
+
+from jinja2.testsuite import JinjaTestCase, dict_loader, \
+ package_loader, filesystem_loader, function_loader, \
+ choice_loader, prefix_loader
+
+from jinja2 import Environment, loaders
+from jinja2.loaders import split_template_path
+from jinja2.exceptions import TemplateNotFound
+
+
+class LoaderTestCase(JinjaTestCase):
+
+ def test_dict_loader(self):
+ env = Environment(loader=dict_loader)
+ tmpl = env.get_template('justdict.html')
+ assert tmpl.render().strip() == 'FOO'
+ self.assert_raises(TemplateNotFound, env.get_template, 'missing.html')
+
+ def test_package_loader(self):
+ env = Environment(loader=package_loader)
+ tmpl = env.get_template('test.html')
+ assert tmpl.render().strip() == 'BAR'
+ self.assert_raises(TemplateNotFound, env.get_template, 'missing.html')
+
+ def test_filesystem_loader(self):
+ env = Environment(loader=filesystem_loader)
+ tmpl = env.get_template('test.html')
+ assert tmpl.render().strip() == 'BAR'
+ tmpl = env.get_template('foo/test.html')
+ assert tmpl.render().strip() == 'FOO'
+ self.assert_raises(TemplateNotFound, env.get_template, 'missing.html')
+
+ def test_choice_loader(self):
+ env = Environment(loader=choice_loader)
+ tmpl = env.get_template('justdict.html')
+ assert tmpl.render().strip() == 'FOO'
+ tmpl = env.get_template('test.html')
+ assert tmpl.render().strip() == 'BAR'
+ self.assert_raises(TemplateNotFound, env.get_template, 'missing.html')
+
+ def test_function_loader(self):
+ env = Environment(loader=function_loader)
+ tmpl = env.get_template('justfunction.html')
+ assert tmpl.render().strip() == 'FOO'
+ self.assert_raises(TemplateNotFound, env.get_template, 'missing.html')
+
+ def test_prefix_loader(self):
+ env = Environment(loader=prefix_loader)
+ tmpl = env.get_template('a/test.html')
+ assert tmpl.render().strip() == 'BAR'
+ tmpl = env.get_template('b/justdict.html')
+ assert tmpl.render().strip() == 'FOO'
+ self.assert_raises(TemplateNotFound, env.get_template, 'missing')
+
+ def test_caching(self):
+ changed = False
+ class TestLoader(loaders.BaseLoader):
+ def get_source(self, environment, template):
+ return u'foo', None, lambda: not changed
+ env = Environment(loader=TestLoader(), cache_size=-1)
+ tmpl = env.get_template('template')
+ assert tmpl is env.get_template('template')
+ changed = True
+ assert tmpl is not env.get_template('template')
+ changed = False
+
+ env = Environment(loader=TestLoader(), cache_size=0)
+ assert env.get_template('template') \
+ is not env.get_template('template')
+
+ env = Environment(loader=TestLoader(), cache_size=2)
+ t1 = env.get_template('one')
+ t2 = env.get_template('two')
+ assert t2 is env.get_template('two')
+ assert t1 is env.get_template('one')
+ t3 = env.get_template('three')
+ assert 'one' in env.cache
+ assert 'two' not in env.cache
+ assert 'three' in env.cache
+
+ def test_split_template_path(self):
+ assert split_template_path('foo/bar') == ['foo', 'bar']
+ assert split_template_path('./foo/bar') == ['foo', 'bar']
+ self.assert_raises(TemplateNotFound, split_template_path, '../foo')
+
+
+class ModuleLoaderTestCase(JinjaTestCase):
+ archive = None
+
+ def compile_down(self, zip='deflated', py_compile=False):
+ super(ModuleLoaderTestCase, self).setup()
+ log = []
+ self.reg_env = Environment(loader=prefix_loader)
+ if zip is not None:
+ self.archive = tempfile.mkstemp(suffix='.zip')[1]
+ else:
+ self.archive = tempfile.mkdtemp()
+ self.reg_env.compile_templates(self.archive, zip=zip,
+ log_function=log.append,
+ py_compile=py_compile)
+ self.mod_env = Environment(loader=loaders.ModuleLoader(self.archive))
+ return ''.join(log)
+
+ def teardown(self):
+ super(ModuleLoaderTestCase, self).teardown()
+ if hasattr(self, 'mod_env'):
+ if os.path.isfile(self.archive):
+ os.remove(self.archive)
+ else:
+ shutil.rmtree(self.archive)
+ self.archive = None
+
+ def test_log(self):
+ log = self.compile_down()
+ assert 'Compiled "a/foo/test.html" as ' \
+ 'tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a' in log
+ assert 'Finished compiling templates' in log
+ assert 'Could not compile "a/syntaxerror.html": ' \
+ 'Encountered unknown tag \'endif\'' in log
+
+ def _test_common(self):
+ tmpl1 = self.reg_env.get_template('a/test.html')
+ tmpl2 = self.mod_env.get_template('a/test.html')
+ assert tmpl1.render() == tmpl2.render()
+
+ tmpl1 = self.reg_env.get_template('b/justdict.html')
+ tmpl2 = self.mod_env.get_template('b/justdict.html')
+ assert tmpl1.render() == tmpl2.render()
+
+ def test_deflated_zip_compile(self):
+ self.compile_down(zip='deflated')
+ self._test_common()
+
+ def test_stored_zip_compile(self):
+ self.compile_down(zip='stored')
+ self._test_common()
+
+ def test_filesystem_compile(self):
+ self.compile_down(zip=None)
+ self._test_common()
+
+ def test_weak_references(self):
+ self.compile_down()
+ tmpl = self.mod_env.get_template('a/test.html')
+ key = loaders.ModuleLoader.get_template_key('a/test.html')
+ name = self.mod_env.loader.module.__name__
+
+ assert hasattr(self.mod_env.loader.module, key)
+ assert name in sys.modules
+
+ # unset all, ensure the module is gone from sys.modules
+ self.mod_env = tmpl = None
+
+ try:
+ import gc
+ gc.collect()
+ except:
+ pass
+
+ assert name not in sys.modules
+
+ def test_byte_compilation(self):
+ log = self.compile_down(py_compile=True)
+ assert 'Byte-compiled "a/test.html"' in log
+ tmpl1 = self.mod_env.get_template('a/test.html')
+ mod = self.mod_env.loader.module. \
+ tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490
+ assert mod.__file__.endswith('.pyc')
+
+ def test_choice_loader(self):
+ log = self.compile_down(py_compile=True)
+ assert 'Byte-compiled "a/test.html"' in log
+
+ self.mod_env.loader = loaders.ChoiceLoader([
+ self.mod_env.loader,
+ loaders.DictLoader({'DICT_SOURCE': 'DICT_TEMPLATE'})
+ ])
+
+ tmpl1 = self.mod_env.get_template('a/test.html')
+ self.assert_equal(tmpl1.render(), 'BAR')
+ tmpl2 = self.mod_env.get_template('DICT_SOURCE')
+ self.assert_equal(tmpl2.render(), 'DICT_TEMPLATE')
+
+ def test_prefix_loader(self):
+ log = self.compile_down(py_compile=True)
+ assert 'Byte-compiled "a/test.html"' in log
+
+ self.mod_env.loader = loaders.PrefixLoader({
+ 'MOD': self.mod_env.loader,
+ 'DICT': loaders.DictLoader({'test.html': 'DICT_TEMPLATE'})
+ })
+
+ tmpl1 = self.mod_env.get_template('MOD/a/test.html')
+ self.assert_equal(tmpl1.render(), 'BAR')
+ tmpl2 = self.mod_env.get_template('DICT/test.html')
+ self.assert_equal(tmpl2.render(), 'DICT_TEMPLATE')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(LoaderTestCase))
+ suite.addTest(unittest.makeSuite(ModuleLoaderTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/regression.py b/websdk/jinja2/testsuite/regression.py
new file mode 100644
index 0000000..4db9076
--- /dev/null
+++ b/websdk/jinja2/testsuite/regression.py
@@ -0,0 +1,255 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.regression
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests corner cases and bugs.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Template, Environment, DictLoader, TemplateSyntaxError, \
+ TemplateNotFound, PrefixLoader
+
+env = Environment()
+
+
+class CornerTestCase(JinjaTestCase):
+
+ def test_assigned_scoping(self):
+ t = env.from_string('''
+ {%- for item in (1, 2, 3, 4) -%}
+ [{{ item }}]
+ {%- endfor %}
+ {{- item -}}
+ ''')
+ assert t.render(item=42) == '[1][2][3][4]42'
+
+ t = env.from_string('''
+ {%- for item in (1, 2, 3, 4) -%}
+ [{{ item }}]
+ {%- endfor %}
+ {%- set item = 42 %}
+ {{- item -}}
+ ''')
+ assert t.render() == '[1][2][3][4]42'
+
+ t = env.from_string('''
+ {%- set item = 42 %}
+ {%- for item in (1, 2, 3, 4) -%}
+ [{{ item }}]
+ {%- endfor %}
+ {{- item -}}
+ ''')
+ assert t.render() == '[1][2][3][4]42'
+
+ def test_closure_scoping(self):
+ t = env.from_string('''
+ {%- set wrapper = "<FOO>" %}
+ {%- for item in (1, 2, 3, 4) %}
+ {%- macro wrapper() %}[{{ item }}]{% endmacro %}
+ {{- wrapper() }}
+ {%- endfor %}
+ {{- wrapper -}}
+ ''')
+ assert t.render() == '[1][2][3][4]<FOO>'
+
+ t = env.from_string('''
+ {%- for item in (1, 2, 3, 4) %}
+ {%- macro wrapper() %}[{{ item }}]{% endmacro %}
+ {{- wrapper() }}
+ {%- endfor %}
+ {%- set wrapper = "<FOO>" %}
+ {{- wrapper -}}
+ ''')
+ assert t.render() == '[1][2][3][4]<FOO>'
+
+ t = env.from_string('''
+ {%- for item in (1, 2, 3, 4) %}
+ {%- macro wrapper() %}[{{ item }}]{% endmacro %}
+ {{- wrapper() }}
+ {%- endfor %}
+ {{- wrapper -}}
+ ''')
+ assert t.render(wrapper=23) == '[1][2][3][4]23'
+
+
+class BugTestCase(JinjaTestCase):
+
+ def test_keyword_folding(self):
+ env = Environment()
+ env.filters['testing'] = lambda value, some: value + some
+ assert env.from_string("{{ 'test'|testing(some='stuff') }}") \
+ .render() == 'teststuff'
+
+ def test_extends_output_bugs(self):
+ env = Environment(loader=DictLoader({
+ 'parent.html': '(({% block title %}{% endblock %}))'
+ }))
+
+ t = env.from_string('{% if expr %}{% extends "parent.html" %}{% endif %}'
+ '[[{% block title %}title{% endblock %}]]'
+ '{% for item in [1, 2, 3] %}({{ item }}){% endfor %}')
+ assert t.render(expr=False) == '[[title]](1)(2)(3)'
+ assert t.render(expr=True) == '((title))'
+
+ def test_urlize_filter_escaping(self):
+ tmpl = env.from_string('{{ "http://www.example.org/<foo"|urlize }}')
+ assert tmpl.render() == '<a href="http://www.example.org/&lt;foo">http://www.example.org/&lt;foo</a>'
+
+ def test_loop_call_loop(self):
+ tmpl = env.from_string('''
+
+ {% macro test() %}
+ {{ caller() }}
+ {% endmacro %}
+
+ {% for num1 in range(5) %}
+ {% call test() %}
+ {% for num2 in range(10) %}
+ {{ loop.index }}
+ {% endfor %}
+ {% endcall %}
+ {% endfor %}
+
+ ''')
+
+ assert tmpl.render().split() == map(unicode, range(1, 11)) * 5
+
+ def test_weird_inline_comment(self):
+ env = Environment(line_statement_prefix='%')
+ self.assert_raises(TemplateSyntaxError, env.from_string,
+ '% for item in seq {# missing #}\n...% endfor')
+
+ def test_old_macro_loop_scoping_bug(self):
+ tmpl = env.from_string('{% for i in (1, 2) %}{{ i }}{% endfor %}'
+ '{% macro i() %}3{% endmacro %}{{ i() }}')
+ assert tmpl.render() == '123'
+
+ def test_partial_conditional_assignments(self):
+ tmpl = env.from_string('{% if b %}{% set a = 42 %}{% endif %}{{ a }}')
+ assert tmpl.render(a=23) == '23'
+ assert tmpl.render(b=True) == '42'
+
+ def test_stacked_locals_scoping_bug(self):
+ env = Environment(line_statement_prefix='#')
+ t = env.from_string('''\
+# for j in [1, 2]:
+# set x = 1
+# for i in [1, 2]:
+# print x
+# if i % 2 == 0:
+# set x = x + 1
+# endif
+# endfor
+# endfor
+# if a
+# print 'A'
+# elif b
+# print 'B'
+# elif c == d
+# print 'C'
+# else
+# print 'D'
+# endif
+ ''')
+ assert t.render(a=0, b=False, c=42, d=42.0) == '1111C'
+
+ def test_stacked_locals_scoping_bug_twoframe(self):
+ t = Template('''
+ {% set x = 1 %}
+ {% for item in foo %}
+ {% if item == 1 %}
+ {% set x = 2 %}
+ {% endif %}
+ {% endfor %}
+ {{ x }}
+ ''')
+ rv = t.render(foo=[1]).strip()
+ assert rv == u'1'
+
+ def test_call_with_args(self):
+ t = Template("""{% macro dump_users(users) -%}
+ <ul>
+ {%- for user in users -%}
+ <li><p>{{ user.username|e }}</p>{{ caller(user) }}</li>
+ {%- endfor -%}
+ </ul>
+ {%- endmacro -%}
+
+ {% call(user) dump_users(list_of_user) -%}
+ <dl>
+ <dl>Realname</dl>
+ <dd>{{ user.realname|e }}</dd>
+ <dl>Description</dl>
+ <dd>{{ user.description }}</dd>
+ </dl>
+ {% endcall %}""")
+
+ assert [x.strip() for x in t.render(list_of_user=[{
+ 'username':'apo',
+ 'realname':'something else',
+ 'description':'test'
+ }]).splitlines()] == [
+ u'<ul><li><p>apo</p><dl>',
+ u'<dl>Realname</dl>',
+ u'<dd>something else</dd>',
+ u'<dl>Description</dl>',
+ u'<dd>test</dd>',
+ u'</dl>',
+ u'</li></ul>'
+ ]
+
+ def test_empty_if_condition_fails(self):
+ self.assert_raises(TemplateSyntaxError, Template, '{% if %}....{% endif %}')
+ self.assert_raises(TemplateSyntaxError, Template, '{% if foo %}...{% elif %}...{% endif %}')
+ self.assert_raises(TemplateSyntaxError, Template, '{% for x in %}..{% endfor %}')
+
+ def test_recursive_loop_bug(self):
+ tpl1 = Template("""
+ {% for p in foo recursive%}
+ {{p.bar}}
+ {% for f in p.fields recursive%}
+ {{f.baz}}
+ {{p.bar}}
+ {% if f.rec %}
+ {{ loop(f.sub) }}
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ """)
+
+ tpl2 = Template("""
+ {% for p in foo%}
+ {{p.bar}}
+ {% for f in p.fields recursive%}
+ {{f.baz}}
+ {{p.bar}}
+ {% if f.rec %}
+ {{ loop(f.sub) }}
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ """)
+
+ def test_correct_prefix_loader_name(self):
+ env = Environment(loader=PrefixLoader({
+ 'foo': DictLoader({})
+ }))
+ try:
+ env.get_template('foo/bar.html')
+ except TemplateNotFound, e:
+ assert e.name == 'foo/bar.html'
+ else:
+ assert False, 'expected error here'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(CornerTestCase))
+ suite.addTest(unittest.makeSuite(BugTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/res/__init__.py b/websdk/jinja2/testsuite/res/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/websdk/jinja2/testsuite/res/__init__.py
diff --git a/websdk/jinja2/testsuite/res/templates/broken.html b/websdk/jinja2/testsuite/res/templates/broken.html
new file mode 100644
index 0000000..77669fa
--- /dev/null
+++ b/websdk/jinja2/testsuite/res/templates/broken.html
@@ -0,0 +1,3 @@
+Before
+{{ fail() }}
+After
diff --git a/websdk/jinja2/testsuite/res/templates/foo/test.html b/websdk/jinja2/testsuite/res/templates/foo/test.html
new file mode 100644
index 0000000..b7d6715
--- /dev/null
+++ b/websdk/jinja2/testsuite/res/templates/foo/test.html
@@ -0,0 +1 @@
+FOO
diff --git a/websdk/jinja2/testsuite/res/templates/syntaxerror.html b/websdk/jinja2/testsuite/res/templates/syntaxerror.html
new file mode 100644
index 0000000..f21b817
--- /dev/null
+++ b/websdk/jinja2/testsuite/res/templates/syntaxerror.html
@@ -0,0 +1,4 @@
+Foo
+{% for item in broken %}
+ ...
+{% endif %}
diff --git a/websdk/jinja2/testsuite/res/templates/test.html b/websdk/jinja2/testsuite/res/templates/test.html
new file mode 100644
index 0000000..ba578e4
--- /dev/null
+++ b/websdk/jinja2/testsuite/res/templates/test.html
@@ -0,0 +1 @@
+BAR
diff --git a/websdk/jinja2/testsuite/security.py b/websdk/jinja2/testsuite/security.py
new file mode 100644
index 0000000..4518eac
--- /dev/null
+++ b/websdk/jinja2/testsuite/security.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.security
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Checks the sandbox and other security features.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Environment
+from jinja2.sandbox import SandboxedEnvironment, \
+ ImmutableSandboxedEnvironment, unsafe
+from jinja2 import Markup, escape
+from jinja2.exceptions import SecurityError, TemplateSyntaxError, \
+ TemplateRuntimeError
+
+
+class PrivateStuff(object):
+
+ def bar(self):
+ return 23
+
+ @unsafe
+ def foo(self):
+ return 42
+
+ def __repr__(self):
+ return 'PrivateStuff'
+
+
+class PublicStuff(object):
+ bar = lambda self: 23
+ _foo = lambda self: 42
+
+ def __repr__(self):
+ return 'PublicStuff'
+
+
+class SandboxTestCase(JinjaTestCase):
+
+ def test_unsafe(self):
+ env = SandboxedEnvironment()
+ self.assert_raises(SecurityError, env.from_string("{{ foo.foo() }}").render,
+ foo=PrivateStuff())
+ self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()), '23')
+
+ self.assert_raises(SecurityError, env.from_string("{{ foo._foo() }}").render,
+ foo=PublicStuff())
+ self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()), '23')
+ self.assert_equal(env.from_string("{{ foo.__class__ }}").render(foo=42), '')
+ self.assert_equal(env.from_string("{{ foo.func_code }}").render(foo=lambda:None), '')
+ # security error comes from __class__ already.
+ self.assert_raises(SecurityError, env.from_string(
+ "{{ foo.__class__.__subclasses__() }}").render, foo=42)
+
+ def test_immutable_environment(self):
+ env = ImmutableSandboxedEnvironment()
+ self.assert_raises(SecurityError, env.from_string(
+ '{{ [].append(23) }}').render)
+ self.assert_raises(SecurityError, env.from_string(
+ '{{ {1:2}.clear() }}').render)
+
+ def test_restricted(self):
+ env = SandboxedEnvironment()
+ self.assert_raises(TemplateSyntaxError, env.from_string,
+ "{% for item.attribute in seq %}...{% endfor %}")
+ self.assert_raises(TemplateSyntaxError, env.from_string,
+ "{% for foo, bar.baz in seq %}...{% endfor %}")
+
+ def test_markup_operations(self):
+ # adding two strings should escape the unsafe one
+ unsafe = '<script type="application/x-some-script">alert("foo");</script>'
+ safe = Markup('<em>username</em>')
+ assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe)
+
+ # string interpolations are safe to use too
+ assert Markup('<em>%s</em>') % '<bad user>' == \
+ '<em>&lt;bad user&gt;</em>'
+ assert Markup('<em>%(username)s</em>') % {
+ 'username': '<bad user>'
+ } == '<em>&lt;bad user&gt;</em>'
+
+ # an escaped object is markup too
+ assert type(Markup('foo') + 'bar') is Markup
+
+ # and it implements __html__ by returning itself
+ x = Markup("foo")
+ assert x.__html__() is x
+
+ # it also knows how to treat __html__ objects
+ class Foo(object):
+ def __html__(self):
+ return '<em>awesome</em>'
+ def __unicode__(self):
+ return 'awesome'
+ assert Markup(Foo()) == '<em>awesome</em>'
+ assert Markup('<strong>%s</strong>') % Foo() == \
+ '<strong><em>awesome</em></strong>'
+
+ # escaping and unescaping
+ assert escape('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
+ assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
+ assert Markup("&lt;test&gt;").unescape() == "<test>"
+
+ def test_template_data(self):
+ env = Environment(autoescape=True)
+ t = env.from_string('{% macro say_hello(name) %}'
+ '<p>Hello {{ name }}!</p>{% endmacro %}'
+ '{{ say_hello("<blink>foo</blink>") }}')
+ escaped_out = '<p>Hello &lt;blink&gt;foo&lt;/blink&gt;!</p>'
+ assert t.render() == escaped_out
+ assert unicode(t.module) == escaped_out
+ assert escape(t.module) == escaped_out
+ assert t.module.say_hello('<blink>foo</blink>') == escaped_out
+ assert escape(t.module.say_hello('<blink>foo</blink>')) == escaped_out
+
+ def test_attr_filter(self):
+ env = SandboxedEnvironment()
+ tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}')
+ self.assert_raises(SecurityError, tmpl.render, cls=int)
+
+ def test_binary_operator_intercepting(self):
+ def disable_op(left, right):
+ raise TemplateRuntimeError('that operator so does not work')
+ for expr, ctx, rv in ('1 + 2', {}, '3'), ('a + 2', {'a': 2}, '4'):
+ env = SandboxedEnvironment()
+ env.binop_table['+'] = disable_op
+ t = env.from_string('{{ %s }}' % expr)
+ assert t.render(ctx) == rv
+ env.intercepted_binops = frozenset(['+'])
+ t = env.from_string('{{ %s }}' % expr)
+ try:
+ t.render(ctx)
+ except TemplateRuntimeError, e:
+ pass
+ else:
+ self.fail('expected runtime error')
+
+ def test_unary_operator_intercepting(self):
+ def disable_op(arg):
+ raise TemplateRuntimeError('that operator so does not work')
+ for expr, ctx, rv in ('-1', {}, '-1'), ('-a', {'a': 2}, '-2'):
+ env = SandboxedEnvironment()
+ env.unop_table['-'] = disable_op
+ t = env.from_string('{{ %s }}' % expr)
+ assert t.render(ctx) == rv
+ env.intercepted_unops = frozenset(['-'])
+ t = env.from_string('{{ %s }}' % expr)
+ try:
+ t.render(ctx)
+ except TemplateRuntimeError, e:
+ pass
+ else:
+ self.fail('expected runtime error')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(SandboxTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/tests.py b/websdk/jinja2/testsuite/tests.py
new file mode 100644
index 0000000..3ece7a8
--- /dev/null
+++ b/websdk/jinja2/testsuite/tests.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.tests
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Who tests the tests?
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Markup, Environment
+
+env = Environment()
+
+
+class TestsTestCase(JinjaTestCase):
+
+ def test_defined(self):
+ tmpl = env.from_string('{{ missing is defined }}|{{ true is defined }}')
+ assert tmpl.render() == 'False|True'
+
+ def test_even(self):
+ tmpl = env.from_string('''{{ 1 is even }}|{{ 2 is even }}''')
+ assert tmpl.render() == 'False|True'
+
+ def test_odd(self):
+ tmpl = env.from_string('''{{ 1 is odd }}|{{ 2 is odd }}''')
+ assert tmpl.render() == 'True|False'
+
+ def test_lower(self):
+ tmpl = env.from_string('''{{ "foo" is lower }}|{{ "FOO" is lower }}''')
+ assert tmpl.render() == 'True|False'
+
+ def test_typechecks(self):
+ tmpl = env.from_string('''
+ {{ 42 is undefined }}
+ {{ 42 is defined }}
+ {{ 42 is none }}
+ {{ none is none }}
+ {{ 42 is number }}
+ {{ 42 is string }}
+ {{ "foo" is string }}
+ {{ "foo" is sequence }}
+ {{ [1] is sequence }}
+ {{ range is callable }}
+ {{ 42 is callable }}
+ {{ range(5) is iterable }}
+ {{ {} is mapping }}
+ {{ mydict is mapping }}
+ {{ [] is mapping }}
+ ''')
+ class MyDict(dict):
+ pass
+ assert tmpl.render(mydict=MyDict()).split() == [
+ 'False', 'True', 'False', 'True', 'True', 'False',
+ 'True', 'True', 'True', 'True', 'False', 'True',
+ 'True', 'True', 'False'
+ ]
+
+ def test_sequence(self):
+ tmpl = env.from_string(
+ '{{ [1, 2, 3] is sequence }}|'
+ '{{ "foo" is sequence }}|'
+ '{{ 42 is sequence }}'
+ )
+ assert tmpl.render() == 'True|True|False'
+
+ def test_upper(self):
+ tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}')
+ assert tmpl.render() == 'True|False'
+
+ def test_sameas(self):
+ tmpl = env.from_string('{{ foo is sameas false }}|'
+ '{{ 0 is sameas false }}')
+ assert tmpl.render(foo=False) == 'True|False'
+
+ def test_no_paren_for_arg1(self):
+ tmpl = env.from_string('{{ foo is sameas none }}')
+ assert tmpl.render(foo=None) == 'True'
+
+ def test_escaped(self):
+ env = Environment(autoescape=True)
+ tmpl = env.from_string('{{ x is escaped }}|{{ y is escaped }}')
+ assert tmpl.render(x='foo', y=Markup('foo')) == 'False|True'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestsTestCase))
+ return suite
diff --git a/websdk/jinja2/testsuite/utils.py b/websdk/jinja2/testsuite/utils.py
new file mode 100644
index 0000000..be2e902
--- /dev/null
+++ b/websdk/jinja2/testsuite/utils.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.testsuite.utils
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests utilities jinja uses.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import gc
+import unittest
+
+import pickle
+
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2.utils import LRUCache, escape, object_type_repr
+
+
+class LRUCacheTestCase(JinjaTestCase):
+
+ def test_simple(self):
+ d = LRUCache(3)
+ d["a"] = 1
+ d["b"] = 2
+ d["c"] = 3
+ d["a"]
+ d["d"] = 4
+ assert len(d) == 3
+ assert 'a' in d and 'c' in d and 'd' in d and 'b' not in d
+
+ def test_pickleable(self):
+ cache = LRUCache(2)
+ cache["foo"] = 42
+ cache["bar"] = 23
+ cache["foo"]
+
+ for protocol in range(3):
+ copy = pickle.loads(pickle.dumps(cache, protocol))
+ assert copy.capacity == cache.capacity
+ assert copy._mapping == cache._mapping
+ assert copy._queue == cache._queue
+
+
+class HelpersTestCase(JinjaTestCase):
+
+ def test_object_type_repr(self):
+ class X(object):
+ pass
+ self.assert_equal(object_type_repr(42), 'int object')
+ self.assert_equal(object_type_repr([]), 'list object')
+ self.assert_equal(object_type_repr(X()),
+ 'jinja2.testsuite.utils.X object')
+ self.assert_equal(object_type_repr(None), 'None')
+ self.assert_equal(object_type_repr(Ellipsis), 'Ellipsis')
+
+
+class MarkupLeakTestCase(JinjaTestCase):
+
+ def test_markup_leaks(self):
+ counts = set()
+ for count in xrange(20):
+ for item in xrange(1000):
+ escape("foo")
+ escape("<foo>")
+ escape(u"foo")
+ escape(u"<foo>")
+ counts.add(len(gc.get_objects()))
+ assert len(counts) == 1, 'ouch, c extension seems to leak objects'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(LRUCacheTestCase))
+ suite.addTest(unittest.makeSuite(HelpersTestCase))
+
+ # this test only tests the c extension
+ if not hasattr(escape, 'func_code'):
+ suite.addTest(unittest.makeSuite(MarkupLeakTestCase))
+
+ return suite
diff --git a/websdk/jinja2/utils.py b/websdk/jinja2/utils.py
new file mode 100644
index 0000000..49e9e9a
--- /dev/null
+++ b/websdk/jinja2/utils.py
@@ -0,0 +1,601 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.utils
+ ~~~~~~~~~~~~
+
+ Utility functions.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+import sys
+import errno
+try:
+ from thread import allocate_lock
+except ImportError:
+ from dummy_thread import allocate_lock
+from collections import deque
+from itertools import imap
+
+
+_word_split_re = re.compile(r'(\s+)')
+_punctuation_re = re.compile(
+ '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
+ '|'.join(imap(re.escape, ('(', '<', '&lt;'))),
+ '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
+ )
+)
+_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
+_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
+_entity_re = re.compile(r'&([^;]+);')
+_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+_digits = '0123456789'
+
+# special singleton representing missing values for the runtime
+missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
+
+# internal code
+internal_code = set()
+
+
+# concatenate a list of strings and convert them to unicode.
+# unfortunately there is a bug in python 2.4 and lower that causes
+# unicode.join trash the traceback.
+_concat = u''.join
+try:
+ def _test_gen_bug():
+ raise TypeError(_test_gen_bug)
+ yield None
+ _concat(_test_gen_bug())
+except TypeError, _error:
+ if not _error.args or _error.args[0] is not _test_gen_bug:
+ def concat(gen):
+ try:
+ return _concat(list(gen))
+ except Exception:
+ # this hack is needed so that the current frame
+ # does not show up in the traceback.
+ exc_type, exc_value, tb = sys.exc_info()
+ raise exc_type, exc_value, tb.tb_next
+ else:
+ concat = _concat
+ del _test_gen_bug, _error
+
+
+# for python 2.x we create outselves a next() function that does the
+# basics without exception catching.
+try:
+ next = next
+except NameError:
+ def next(x):
+ return x.next()
+
+
+# if this python version is unable to deal with unicode filenames
+# when passed to encode we let this function encode it properly.
+# This is used in a couple of places. As far as Jinja is concerned
+# filenames are unicode *or* bytestrings in 2.x and unicode only in
+# 3.x because compile cannot handle bytes
+if sys.version_info < (3, 0):
+ def _encode_filename(filename):
+ if isinstance(filename, unicode):
+ return filename.encode('utf-8')
+ return filename
+else:
+ def _encode_filename(filename):
+ assert filename is None or isinstance(filename, str), \
+ 'filenames must be strings'
+ return filename
+
+from keyword import iskeyword as is_python_keyword
+
+
+# common types. These do exist in the special types module too which however
+# does not exist in IronPython out of the box. Also that way we don't have
+# to deal with implementation specific stuff here
+class _C(object):
+ def method(self): pass
+def _func():
+ yield None
+FunctionType = type(_func)
+GeneratorType = type(_func())
+MethodType = type(_C.method)
+CodeType = type(_C.method.func_code)
+try:
+ raise TypeError()
+except TypeError:
+ _tb = sys.exc_info()[2]
+ TracebackType = type(_tb)
+ FrameType = type(_tb.tb_frame)
+del _C, _tb, _func
+
+
+def contextfunction(f):
+ """This decorator can be used to mark a function or method context callable.
+ A context callable is passed the active :class:`Context` as first argument when
+ called from the template. This is useful if a function wants to get access
+ to the context or functions provided on the context object. For example
+ a function that returns a sorted list of template variables the current
+ template exports could look like this::
+
+ @contextfunction
+ def get_exported_names(context):
+ return sorted(context.exported_vars)
+ """
+ f.contextfunction = True
+ return f
+
+
+def evalcontextfunction(f):
+ """This decoraotr can be used to mark a function or method as an eval
+ context callable. This is similar to the :func:`contextfunction`
+ but instead of passing the context, an evaluation context object is
+ passed. For more information about the eval context, see
+ :ref:`eval-context`.
+
+ .. versionadded:: 2.4
+ """
+ f.evalcontextfunction = True
+ return f
+
+
+def environmentfunction(f):
+ """This decorator can be used to mark a function or method as environment
+ callable. This decorator works exactly like the :func:`contextfunction`
+ decorator just that the first argument is the active :class:`Environment`
+ and not context.
+ """
+ f.environmentfunction = True
+ return f
+
+
+def internalcode(f):
+ """Marks the function as internally used"""
+ internal_code.add(f.func_code)
+ return f
+
+
+def is_undefined(obj):
+ """Check if the object passed is undefined. This does nothing more than
+ performing an instance check against :class:`Undefined` but looks nicer.
+ This can be used for custom filters or tests that want to react to
+ undefined variables. For example a custom default filter can look like
+ this::
+
+ def default(var, default=''):
+ if is_undefined(var):
+ return default
+ return var
+ """
+ from jinja2.runtime import Undefined
+ return isinstance(obj, Undefined)
+
+
+def consume(iterable):
+ """Consumes an iterable without doing anything with it."""
+ for event in iterable:
+ pass
+
+
+def clear_caches():
+ """Jinja2 keeps internal caches for environments and lexers. These are
+ used so that Jinja2 doesn't have to recreate environments and lexers all
+ the time. Normally you don't have to care about that but if you are
+ messuring memory consumption you may want to clean the caches.
+ """
+ from jinja2.environment import _spontaneous_environments
+ from jinja2.lexer import _lexer_cache
+ _spontaneous_environments.clear()
+ _lexer_cache.clear()
+
+
+def import_string(import_name, silent=False):
+ """Imports an object based on a string. This use useful if you want to
+ use import paths as endpoints or something similar. An import path can
+ be specified either in dotted notation (``xml.sax.saxutils.escape``)
+ or with a colon as object delimiter (``xml.sax.saxutils:escape``).
+
+ If the `silent` is True the return value will be `None` if the import
+ fails.
+
+ :return: imported object
+ """
+ try:
+ if ':' in import_name:
+ module, obj = import_name.split(':', 1)
+ elif '.' in import_name:
+ items = import_name.split('.')
+ module = '.'.join(items[:-1])
+ obj = items[-1]
+ else:
+ return __import__(import_name)
+ return getattr(__import__(module, None, None, [obj]), obj)
+ except (ImportError, AttributeError):
+ if not silent:
+ raise
+
+
+def open_if_exists(filename, mode='rb'):
+ """Returns a file descriptor for the filename if that file exists,
+ otherwise `None`.
+ """
+ try:
+ return open(filename, mode)
+ except IOError, e:
+ if e.errno not in (errno.ENOENT, errno.EISDIR):
+ raise
+
+
+def object_type_repr(obj):
+ """Returns the name of the object's type. For some recognized
+ singletons the name of the object is returned instead. (For
+ example for `None` and `Ellipsis`).
+ """
+ if obj is None:
+ return 'None'
+ elif obj is Ellipsis:
+ return 'Ellipsis'
+ # __builtin__ in 2.x, builtins in 3.x
+ if obj.__class__.__module__ in ('__builtin__', 'builtins'):
+ name = obj.__class__.__name__
+ else:
+ name = obj.__class__.__module__ + '.' + obj.__class__.__name__
+ return '%s object' % name
+
+
+def pformat(obj, verbose=False):
+ """Prettyprint an object. Either use the `pretty` library or the
+ builtin `pprint`.
+ """
+ try:
+ from pretty import pretty
+ return pretty(obj, verbose=verbose)
+ except ImportError:
+ from pprint import pformat
+ return pformat(obj)
+
+
+def urlize(text, trim_url_limit=None, nofollow=False):
+ """Converts any URLs in text into clickable links. Works on http://,
+ https:// and www. links. Links can have trailing punctuation (periods,
+ commas, close-parens) and leading punctuation (opening parens) and
+ it'll still do the right thing.
+
+ If trim_url_limit is not None, the URLs in link text will be limited
+ to trim_url_limit characters.
+
+ If nofollow is True, the URLs in link text will get a rel="nofollow"
+ attribute.
+ """
+ trim_url = lambda x, limit=trim_url_limit: limit is not None \
+ and (x[:limit] + (len(x) >=limit and '...'
+ or '')) or x
+ words = _word_split_re.split(unicode(escape(text)))
+ nofollow_attr = nofollow and ' rel="nofollow"' or ''
+ for i, word in enumerate(words):
+ match = _punctuation_re.match(word)
+ if match:
+ lead, middle, trail = match.groups()
+ if middle.startswith('www.') or (
+ '@' not in middle and
+ not middle.startswith('http://') and
+ len(middle) > 0 and
+ middle[0] in _letters + _digits and (
+ middle.endswith('.org') or
+ middle.endswith('.net') or
+ middle.endswith('.com')
+ )):
+ middle = '<a href="http://%s"%s>%s</a>' % (middle,
+ nofollow_attr, trim_url(middle))
+ if middle.startswith('http://') or \
+ middle.startswith('https://'):
+ middle = '<a href="%s"%s>%s</a>' % (middle,
+ nofollow_attr, trim_url(middle))
+ if '@' in middle and not middle.startswith('www.') and \
+ not ':' in middle and _simple_email_re.match(middle):
+ middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
+ if lead + middle + trail != word:
+ words[i] = lead + middle + trail
+ return u''.join(words)
+
+
+def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
+ """Generate some lorem impsum for the template."""
+ from jinja2.constants import LOREM_IPSUM_WORDS
+ from random import choice, randrange
+ words = LOREM_IPSUM_WORDS.split()
+ result = []
+
+ for _ in xrange(n):
+ next_capitalized = True
+ last_comma = last_fullstop = 0
+ word = None
+ last = None
+ p = []
+
+ # each paragraph contains out of 20 to 100 words.
+ for idx, _ in enumerate(xrange(randrange(min, max))):
+ while True:
+ word = choice(words)
+ if word != last:
+ last = word
+ break
+ if next_capitalized:
+ word = word.capitalize()
+ next_capitalized = False
+ # add commas
+ if idx - randrange(3, 8) > last_comma:
+ last_comma = idx
+ last_fullstop += 2
+ word += ','
+ # add end of sentences
+ if idx - randrange(10, 20) > last_fullstop:
+ last_comma = last_fullstop = idx
+ word += '.'
+ next_capitalized = True
+ p.append(word)
+
+ # ensure that the paragraph ends with a dot.
+ p = u' '.join(p)
+ if p.endswith(','):
+ p = p[:-1] + '.'
+ elif not p.endswith('.'):
+ p += '.'
+ result.append(p)
+
+ if not html:
+ return u'\n\n'.join(result)
+ return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
+
+
+class LRUCache(object):
+ """A simple LRU Cache implementation."""
+
+ # this is fast for small capacities (something below 1000) but doesn't
+ # scale. But as long as it's only used as storage for templates this
+ # won't do any harm.
+
+ def __init__(self, capacity):
+ self.capacity = capacity
+ self._mapping = {}
+ self._queue = deque()
+ self._postinit()
+
+ def _postinit(self):
+ # alias all queue methods for faster lookup
+ self._popleft = self._queue.popleft
+ self._pop = self._queue.pop
+ if hasattr(self._queue, 'remove'):
+ self._remove = self._queue.remove
+ self._wlock = allocate_lock()
+ self._append = self._queue.append
+
+ def _remove(self, obj):
+ """Python 2.4 compatibility."""
+ for idx, item in enumerate(self._queue):
+ if item == obj:
+ del self._queue[idx]
+ break
+
+ def __getstate__(self):
+ return {
+ 'capacity': self.capacity,
+ '_mapping': self._mapping,
+ '_queue': self._queue
+ }
+
+ def __setstate__(self, d):
+ self.__dict__.update(d)
+ self._postinit()
+
+ def __getnewargs__(self):
+ return (self.capacity,)
+
+ def copy(self):
+ """Return an shallow copy of the instance."""
+ rv = self.__class__(self.capacity)
+ rv._mapping.update(self._mapping)
+ rv._queue = deque(self._queue)
+ return rv
+
+ def get(self, key, default=None):
+ """Return an item from the cache dict or `default`"""
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
+ def setdefault(self, key, default=None):
+ """Set `default` if the key is not in the cache otherwise
+ leave unchanged. Return the value of this key.
+ """
+ try:
+ return self[key]
+ except KeyError:
+ self[key] = default
+ return default
+
+ def clear(self):
+ """Clear the cache."""
+ self._wlock.acquire()
+ try:
+ self._mapping.clear()
+ self._queue.clear()
+ finally:
+ self._wlock.release()
+
+ def __contains__(self, key):
+ """Check if a key exists in this cache."""
+ return key in self._mapping
+
+ def __len__(self):
+ """Return the current size of the cache."""
+ return len(self._mapping)
+
+ def __repr__(self):
+ return '<%s %r>' % (
+ self.__class__.__name__,
+ self._mapping
+ )
+
+ def __getitem__(self, key):
+ """Get an item from the cache. Moves the item up so that it has the
+ highest priority then.
+
+ Raise an `KeyError` if it does not exist.
+ """
+ rv = self._mapping[key]
+ if self._queue[-1] != key:
+ try:
+ self._remove(key)
+ except ValueError:
+ # if something removed the key from the container
+ # when we read, ignore the ValueError that we would
+ # get otherwise.
+ pass
+ self._append(key)
+ return rv
+
+ def __setitem__(self, key, value):
+ """Sets the value for an item. Moves the item up so that it
+ has the highest priority then.
+ """
+ self._wlock.acquire()
+ try:
+ if key in self._mapping:
+ try:
+ self._remove(key)
+ except ValueError:
+ # __getitem__ is not locked, it might happen
+ pass
+ elif len(self._mapping) == self.capacity:
+ del self._mapping[self._popleft()]
+ self._append(key)
+ self._mapping[key] = value
+ finally:
+ self._wlock.release()
+
+ def __delitem__(self, key):
+ """Remove an item from the cache dict.
+ Raise an `KeyError` if it does not exist.
+ """
+ self._wlock.acquire()
+ try:
+ del self._mapping[key]
+ try:
+ self._remove(key)
+ except ValueError:
+ # __getitem__ is not locked, it might happen
+ pass
+ finally:
+ self._wlock.release()
+
+ def items(self):
+ """Return a list of items."""
+ result = [(key, self._mapping[key]) for key in list(self._queue)]
+ result.reverse()
+ return result
+
+ def iteritems(self):
+ """Iterate over all items."""
+ return iter(self.items())
+
+ def values(self):
+ """Return a list of all values."""
+ return [x[1] for x in self.items()]
+
+ def itervalue(self):
+ """Iterate over all values."""
+ return iter(self.values())
+
+ def keys(self):
+ """Return a list of all keys ordered by most recent usage."""
+ return list(self)
+
+ def iterkeys(self):
+ """Iterate over all keys in the cache dict, ordered by
+ the most recent usage.
+ """
+ return reversed(tuple(self._queue))
+
+ __iter__ = iterkeys
+
+ def __reversed__(self):
+ """Iterate over the values in the cache dict, oldest items
+ coming first.
+ """
+ return iter(tuple(self._queue))
+
+ __copy__ = copy
+
+
+# register the LRU cache as mutable mapping if possible
+try:
+ from collections import MutableMapping
+ MutableMapping.register(LRUCache)
+except ImportError:
+ pass
+
+
+class Cycler(object):
+ """A cycle helper for templates."""
+
+ def __init__(self, *items):
+ if not items:
+ raise RuntimeError('at least one item has to be provided')
+ self.items = items
+ self.reset()
+
+ def reset(self):
+ """Resets the cycle."""
+ self.pos = 0
+
+ @property
+ def current(self):
+ """Returns the current item."""
+ return self.items[self.pos]
+
+ def next(self):
+ """Goes one item ahead and returns it."""
+ rv = self.current
+ self.pos = (self.pos + 1) % len(self.items)
+ return rv
+
+
+class Joiner(object):
+ """A joining helper for templates."""
+
+ def __init__(self, sep=u', '):
+ self.sep = sep
+ self.used = False
+
+ def __call__(self):
+ if not self.used:
+ self.used = True
+ return u''
+ return self.sep
+
+
+# try markupsafe first, if that fails go with Jinja2's bundled version
+# of markupsafe. Markupsafe was previously Jinja2's implementation of
+# the Markup object but was moved into a separate package in a patchleve
+# release
+try:
+ from markupsafe import Markup, escape, soft_unicode
+except ImportError:
+ from jinja2._markupsafe import Markup, escape, soft_unicode
+
+
+# partials
+try:
+ from functools import partial
+except ImportError:
+ class partial(object):
+ def __init__(self, _func, *args, **kwargs):
+ self._func = _func
+ self._args = args
+ self._kwargs = kwargs
+ def __call__(self, *args, **kwargs):
+ kwargs.update(self._kwargs)
+ return self._func(*(self._args + args), **kwargs)
diff --git a/websdk/jinja2/visitor.py b/websdk/jinja2/visitor.py
new file mode 100644
index 0000000..413e7c3
--- /dev/null
+++ b/websdk/jinja2/visitor.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+"""
+ jinja2.visitor
+ ~~~~~~~~~~~~~~
+
+ This module implements a visitor for the nodes.
+
+ :copyright: (c) 2010 by the Jinja Team.
+ :license: BSD.
+"""
+from jinja2.nodes import Node
+
+
+class NodeVisitor(object):
+ """Walks the abstract syntax tree and call visitor functions for every
+ node found. The visitor functions may return values which will be
+ forwarded by the `visit` method.
+
+ Per default the visitor functions for the nodes are ``'visit_'`` +
+ class name of the node. So a `TryFinally` node visit function would
+ be `visit_TryFinally`. This behavior can be changed by overriding
+ the `get_visitor` function. If no visitor function exists for a node
+ (return value `None`) the `generic_visit` visitor is used instead.
+ """
+
+ def get_visitor(self, node):
+ """Return the visitor function for this node or `None` if no visitor
+ exists for this node. In that case the generic visit function is
+ used instead.
+ """
+ method = 'visit_' + node.__class__.__name__
+ return getattr(self, method, None)
+
+ def visit(self, node, *args, **kwargs):
+ """Visit a node."""
+ f = self.get_visitor(node)
+ if f is not None:
+ return f(node, *args, **kwargs)
+ return self.generic_visit(node, *args, **kwargs)
+
+ def generic_visit(self, node, *args, **kwargs):
+ """Called if no explicit visitor function exists for a node."""
+ for node in node.iter_child_nodes():
+ self.visit(node, *args, **kwargs)
+
+
+class NodeTransformer(NodeVisitor):
+ """Walks the abstract syntax tree and allows modifications of nodes.
+
+ The `NodeTransformer` will walk the AST and use the return value of the
+ visitor functions to replace or remove the old node. If the return
+ value of the visitor function is `None` the node will be removed
+ from the previous location otherwise it's replaced with the return
+ value. The return value may be the original node in which case no
+ replacement takes place.
+ """
+
+ def generic_visit(self, node, *args, **kwargs):
+ for field, old_value in node.iter_fields():
+ if isinstance(old_value, list):
+ new_values = []
+ for value in old_value:
+ if isinstance(value, Node):
+ value = self.visit(value, *args, **kwargs)
+ if value is None:
+ continue
+ elif not isinstance(value, Node):
+ new_values.extend(value)
+ continue
+ new_values.append(value)
+ old_value[:] = new_values
+ elif isinstance(old_value, Node):
+ new_node = self.visit(old_value, *args, **kwargs)
+ if new_node is None:
+ delattr(node, field)
+ else:
+ setattr(node, field, new_node)
+ return node
+
+ def visit_list(self, node, *args, **kwargs):
+ """As transformers may return lists in some places this method
+ can be used to enforce a list as return value.
+ """
+ rv = self.visit(node, *args, **kwargs)
+ if not isinstance(rv, list):
+ rv = [rv]
+ return rv
diff --git a/websdk/static/css/main.css b/websdk/static/css/main.css
new file mode 100644
index 0000000..f7cbf25
--- /dev/null
+++ b/websdk/static/css/main.css
@@ -0,0 +1,118 @@
+body {
+ background-color: #c0c0c0;
+ height: 99%;
+ 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: 40px;
+}
+
+input.btn {
+ color: white;
+ /* background-color: #808080; */
+}
+
+#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:20px;
+ padding-top:5px;
+}
+
+#filer ul li a span {
+ height: 56px;
+ display: none;
+}
+#filer ul li a img {
+ height: 56px;
+}
+
+#filer-header {
+ background-color: #808080;
+ color: white;
+ margin: 0;
+ padding: 0;
+ padding-top: 7px;
+ padding-bottom: 7px;
+ width: 100%;
+}
+
+#filer-header span{
+ padding-left: 10px;
+}
diff --git a/websdk/static/css/sugar-theme/images/ui-bg_flat_100_c0c0c0_40x100.png b/websdk/static/css/sugar-theme/images/ui-bg_flat_100_c0c0c0_40x100.png
new file mode 100644
index 0000000..299f267
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-bg_flat_100_c0c0c0_40x100.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-bg_flat_50_aaaaaa_40x100.png b/websdk/static/css/sugar-theme/images/ui-bg_flat_50_aaaaaa_40x100.png
new file mode 100644
index 0000000..5b5dab2
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-bg_flat_50_aaaaaa_40x100.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-bg_flat_65_ffffff_40x100.png b/websdk/static/css/sugar-theme/images/ui-bg_flat_65_ffffff_40x100.png
new file mode 100644
index 0000000..ac8b229
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-bg_flat_65_ffffff_40x100.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-bg_flat_75_282828_40x100.png b/websdk/static/css/sugar-theme/images/ui-bg_flat_75_282828_40x100.png
new file mode 100644
index 0000000..89c6362
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-bg_flat_75_282828_40x100.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-bg_flat_75_808080_40x100.png b/websdk/static/css/sugar-theme/images/ui-bg_flat_75_808080_40x100.png
new file mode 100644
index 0000000..6864463
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-bg_flat_75_808080_40x100.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-bg_glow-ball_20_282828_600x600.png b/websdk/static/css/sugar-theme/images/ui-bg_glow-ball_20_282828_600x600.png
new file mode 100644
index 0000000..d05eb5b
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-bg_glow-ball_20_282828_600x600.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-bg_highlight-hard_5_282828_1x100.png b/websdk/static/css/sugar-theme/images/ui-bg_highlight-hard_5_282828_1x100.png
new file mode 100644
index 0000000..68a36c5
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-bg_highlight-hard_5_282828_1x100.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-bg_highlight-hard_95_c0c0c0_1x100.png b/websdk/static/css/sugar-theme/images/ui-bg_highlight-hard_95_c0c0c0_1x100.png
new file mode 100644
index 0000000..81722a4
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-bg_highlight-hard_95_c0c0c0_1x100.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-icons_000000_256x240.png b/websdk/static/css/sugar-theme/images/ui-icons_000000_256x240.png
new file mode 100644
index 0000000..7c211aa
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-icons_000000_256x240.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-icons_2e83ff_256x240.png b/websdk/static/css/sugar-theme/images/ui-icons_2e83ff_256x240.png
new file mode 100644
index 0000000..09d1cdc
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-icons_2e83ff_256x240.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-icons_cd0a0a_256x240.png b/websdk/static/css/sugar-theme/images/ui-icons_cd0a0a_256x240.png
new file mode 100644
index 0000000..2ab019b
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-icons_cd0a0a_256x240.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/images/ui-icons_ffffff_256x240.png b/websdk/static/css/sugar-theme/images/ui-icons_ffffff_256x240.png
new file mode 100644
index 0000000..42f8f99
--- /dev/null
+++ b/websdk/static/css/sugar-theme/images/ui-icons_ffffff_256x240.png
Binary files differ
diff --git a/websdk/static/css/sugar-theme/jquery-ui-1.8.15.custom.css b/websdk/static/css/sugar-theme/jquery-ui-1.8.15.custom.css
new file mode 100644
index 0000000..2409c32
--- /dev/null
+++ b/websdk/static/css/sugar-theme/jquery-ui-1.8.15.custom.css
@@ -0,0 +1,568 @@
+/*
+ * jQuery UI CSS Framework 1.8.15
+ *
+ * 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.15
+ *
+ * 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=c0c0c0&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: .9em; }
+.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: #c0c0c0 url(images/ui-bg_flat_100_c0c0c0_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: 12px; -webkit-border-top-left-radius: 12px; -khtml-border-top-left-radius: 12px; border-top-left-radius: 12px; }
+.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 12px; -webkit-border-top-right-radius: 12px; -khtml-border-top-right-radius: 12px; border-top-right-radius: 12px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 12px; -webkit-border-bottom-left-radius: 12px; -khtml-border-bottom-left-radius: 12px; border-bottom-left-radius: 12px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 12px; -webkit-border-bottom-right-radius: 12px; -khtml-border-bottom-right-radius: 12px; border-bottom-right-radius: 12px; }
+
+/* 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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-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; margin-bottom: .6em; }
+
+/*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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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/websdk/static/icons b/websdk/static/icons
new file mode 120000
index 0000000..9a5906b
--- /dev/null
+++ b/websdk/static/icons
@@ -0,0 +1 @@
+../../icons/ \ No newline at end of file
diff --git a/websdk/static/images/throbber-120.gif b/websdk/static/images/throbber-120.gif
new file mode 100644
index 0000000..a7d0fc8
--- /dev/null
+++ b/websdk/static/images/throbber-120.gif
Binary files differ
diff --git a/websdk/static/init.html b/websdk/static/init.html
new file mode 100644
index 0000000..6515534
--- /dev/null
+++ b/websdk/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-120.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/websdk/static/js/ace/ace-uncompressed.js b/websdk/static/js/ace/ace-uncompressed.js
new file mode 100644
index 0000000..389daf2
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/ace.js b/websdk/static/js/ace/ace.js
new file mode 100644
index 0000000..7ac56c0
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/cockpit-uncompressed.js b/websdk/static/js/ace/cockpit-uncompressed.js
new file mode 100644
index 0000000..2b75f23
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/cockpit.js b/websdk/static/js/ace/cockpit.js
new file mode 100644
index 0000000..ecdc74c
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/keybinding-emacs.js b/websdk/static/js/ace/keybinding-emacs.js
new file mode 100644
index 0000000..da4aa9d
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/keybinding-vim.js b/websdk/static/js/ace/keybinding-vim.js
new file mode 100644
index 0000000..9f6ae87
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-c_cpp.js b/websdk/static/js/ace/mode-c_cpp.js
new file mode 100644
index 0000000..b19fc07
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-clojure.js b/websdk/static/js/ace/mode-clojure.js
new file mode 100644
index 0000000..91338d2
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-coffee.js b/websdk/static/js/ace/mode-coffee.js
new file mode 100644
index 0000000..ca47332
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-csharp.js b/websdk/static/js/ace/mode-csharp.js
new file mode 100644
index 0000000..4c3c12d
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-css.js b/websdk/static/js/ace/mode-css.js
new file mode 100644
index 0000000..c27ad88
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-html.js b/websdk/static/js/ace/mode-html.js
new file mode 100644
index 0000000..526a1cb
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-java.js b/websdk/static/js/ace/mode-java.js
new file mode 100644
index 0000000..93fc3b9
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-javascript.js b/websdk/static/js/ace/mode-javascript.js
new file mode 100644
index 0000000..f365db4
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-json.js b/websdk/static/js/ace/mode-json.js
new file mode 100644
index 0000000..b4561f6
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-perl.js b/websdk/static/js/ace/mode-perl.js
new file mode 100644
index 0000000..3ec8330
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-php.js b/websdk/static/js/ace/mode-php.js
new file mode 100644
index 0000000..161648b
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-python.js b/websdk/static/js/ace/mode-python.js
new file mode 100644
index 0000000..4c680fa
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-ruby.js b/websdk/static/js/ace/mode-ruby.js
new file mode 100644
index 0000000..7b185e0
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-scss.js b/websdk/static/js/ace/mode-scss.js
new file mode 100644
index 0000000..49a9eb1
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-svg.js b/websdk/static/js/ace/mode-svg.js
new file mode 100644
index 0000000..700e4bb
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/mode-xml.js b/websdk/static/js/ace/mode-xml.js
new file mode 100644
index 0000000..bca8943
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-clouds.js b/websdk/static/js/ace/theme-clouds.js
new file mode 100644
index 0000000..c14d3cb
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-clouds_midnight.js b/websdk/static/js/ace/theme-clouds_midnight.js
new file mode 100644
index 0000000..52137fb
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-cobalt.js b/websdk/static/js/ace/theme-cobalt.js
new file mode 100644
index 0000000..51be7b2
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-dawn.js b/websdk/static/js/ace/theme-dawn.js
new file mode 100644
index 0000000..3b98416
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-eclipse.js b/websdk/static/js/ace/theme-eclipse.js
new file mode 100644
index 0000000..e74e173
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-idle_fingers.js b/websdk/static/js/ace/theme-idle_fingers.js
new file mode 100644
index 0000000..40c7b95
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-kr_theme.js b/websdk/static/js/ace/theme-kr_theme.js
new file mode 100644
index 0000000..2fac8ba
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-merbivore.js b/websdk/static/js/ace/theme-merbivore.js
new file mode 100644
index 0000000..407e6f6
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-merbivore_soft.js b/websdk/static/js/ace/theme-merbivore_soft.js
new file mode 100644
index 0000000..5be28c5
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-mono_industrial.js b/websdk/static/js/ace/theme-mono_industrial.js
new file mode 100644
index 0000000..354de6b
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-monokai.js b/websdk/static/js/ace/theme-monokai.js
new file mode 100644
index 0000000..398195f
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-pastel_on_dark.js b/websdk/static/js/ace/theme-pastel_on_dark.js
new file mode 100644
index 0000000..79cdb10
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-twilight.js b/websdk/static/js/ace/theme-twilight.js
new file mode 100644
index 0000000..4072462
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/theme-vibrant_ink.js b/websdk/static/js/ace/theme-vibrant_ink.js
new file mode 100644
index 0000000..ed6ae9e
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/worker-coffee.js b/websdk/static/js/ace/worker-coffee.js
new file mode 100644
index 0000000..1ffa65f
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/worker-css.js b/websdk/static/js/ace/worker-css.js
new file mode 100644
index 0000000..f76ed43
--- /dev/null
+++ b/websdk/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/websdk/static/js/ace/worker-javascript.js b/websdk/static/js/ace/worker-javascript.js
new file mode 100644
index 0000000..1156c45
--- /dev/null
+++ b/websdk/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/websdk/static/js/jquery-1.6.2.js b/websdk/static/js/jquery-1.6.2.js
new file mode 100644
index 0000000..f3201aa
--- /dev/null
+++ b/websdk/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/websdk/static/js/jquery-ui-1.8.15.custom.min.js b/websdk/static/js/jquery-ui-1.8.15.custom.min.js
new file mode 100644
index 0000000..83de870
--- /dev/null
+++ b/websdk/static/js/jquery-ui-1.8.15.custom.min.js
@@ -0,0 +1,790 @@
+/*!
+ * jQuery UI 1.8.15
+ *
+ * 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.15",
+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.15
+ *
+ * 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++)b(d).triggerHandler("remove");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(){b(this).triggerHandler("remove")});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.15
+ *
+ * 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){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){a.originalEvent=
+a.originalEvent||{};if(!a.originalEvent.mouseHandled){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,e=a.which==1,f=typeof this.options.cancel=="string"?b(a.target).closest(this.options.cancel).length:false;if(!e||f||!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(d){return c._mouseMove(d)};this._mouseUpDelegate=function(d){return c._mouseUp(d)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return a.originalEvent.mouseHandled=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.15
+ *
+ * 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.15
+ *
+ * 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;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.15"});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.15
+ *
+ * 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.15"});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.parentsUntil("body").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.parentsUntil("body").unbind("scroll.droppable");
+a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)}}})(jQuery);
+;/*
+ * jQuery UI Resizable 1.8.15
+ *
+ * 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.15"});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.15
+ *
+ * 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.15"})})(jQuery);
+;/*
+ * jQuery UI Sortable 1.8.15
+ *
+ * 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.15"})})(jQuery);
+;/*
+ * jQuery UI Accordion 1.8.15
+ *
+ * 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.15",
+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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.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.15",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.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.15
+ *
+ * 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.15"})})(jQuery);
+;/*
+ * jQuery UI Tabs 1.8.15
+ *
+ * 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.15"});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.15
+ *
+ * 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.15"}});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");H(b.settings,c?c.apply(a,[a,b]):{});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];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));if(c=this._get(a,"onSelect")){e=this._formatDate(a);c.apply(a.input?a.input[0]:null,[e,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.15";window["DP_jQuery_"+B]=d})(jQuery);
+;/*
+ * jQuery UI Progressbar 1.8.15
+ *
+ * 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.15"})})(jQuery);
+;/*
+ * jQuery UI Effects 1.8.15
+ *
+ * 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.15",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});
+c.wrap(b);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(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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.15
+ *
+ * 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/websdk/static/js/jquery.corner.js b/websdk/static/js/jquery.corner.js
new file mode 100644
index 0000000..c416613
--- /dev/null
+++ b/websdk/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/websdk/static/js/jquery.plugin.svgimg.js b/websdk/static/js/jquery.plugin.svgimg.js
new file mode 100644
index 0000000..4e2f9be
--- /dev/null
+++ b/websdk/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/websdk/static/js/jquery.quicksand.js b/websdk/static/js/jquery.quicksand.js
new file mode 100644
index 0000000..626f490
--- /dev/null
+++ b/websdk/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/websdk/static/js/websdk-1.js b/websdk/static/js/websdk-1.js
new file mode 100644
index 0000000..be651e0
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/.svn/entries b/websdk/static/js/wymeditor/iframe/.svn/entries
new file mode 100644
index 0000000..247ae9d
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/entries b/websdk/static/js/wymeditor/iframe/default/.svn/entries
new file mode 100644
index 0000000..cb122d2
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-blockquote.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-blockquote.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h1.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h1.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h2.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h2.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h3.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h3.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h4.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h4.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h5.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h5.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h6.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-h6.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-p.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-p.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-pre.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/prop-base/lbl-pre.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-blockquote.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-blockquote.png.svn-base
new file mode 100644
index 0000000..65ea205
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-blockquote.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h1.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h1.png.svn-base
new file mode 100644
index 0000000..dea1da3
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h1.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h2.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h2.png.svn-base
new file mode 100644
index 0000000..f4b4274
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h2.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h3.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h3.png.svn-base
new file mode 100644
index 0000000..bd99f76
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h3.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h4.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h4.png.svn-base
new file mode 100644
index 0000000..e06ab3d
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h4.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h5.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h5.png.svn-base
new file mode 100644
index 0000000..360fc60
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h5.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h6.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h6.png.svn-base
new file mode 100644
index 0000000..73e644a
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-h6.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-p.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-p.png.svn-base
new file mode 100644
index 0000000..7f2bf34
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-p.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-pre.png.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-pre.png.svn-base
new file mode 100644
index 0000000..e026e47
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/lbl-pre.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.css.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.css.svn-base
new file mode 100644
index 0000000..769520c
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.html.svn-base b/websdk/static/js/wymeditor/iframe/default/.svn/text-base/wymiframe.html.svn-base
new file mode 100644
index 0000000..91a690c
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/lbl-blockquote.png b/websdk/static/js/wymeditor/iframe/default/lbl-blockquote.png
new file mode 100644
index 0000000..65ea205
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/lbl-blockquote.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/lbl-h1.png b/websdk/static/js/wymeditor/iframe/default/lbl-h1.png
new file mode 100644
index 0000000..dea1da3
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/lbl-h1.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/lbl-h2.png b/websdk/static/js/wymeditor/iframe/default/lbl-h2.png
new file mode 100644
index 0000000..f4b4274
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/lbl-h2.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/lbl-h3.png b/websdk/static/js/wymeditor/iframe/default/lbl-h3.png
new file mode 100644
index 0000000..bd99f76
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/lbl-h3.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/lbl-h4.png b/websdk/static/js/wymeditor/iframe/default/lbl-h4.png
new file mode 100644
index 0000000..e06ab3d
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/lbl-h4.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/lbl-h5.png b/websdk/static/js/wymeditor/iframe/default/lbl-h5.png
new file mode 100644
index 0000000..360fc60
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/lbl-h5.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/lbl-h6.png b/websdk/static/js/wymeditor/iframe/default/lbl-h6.png
new file mode 100644
index 0000000..73e644a
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/lbl-h6.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/lbl-p.png b/websdk/static/js/wymeditor/iframe/default/lbl-p.png
new file mode 100644
index 0000000..7f2bf34
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/lbl-p.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/lbl-pre.png b/websdk/static/js/wymeditor/iframe/default/lbl-pre.png
new file mode 100644
index 0000000..e026e47
--- /dev/null
+++ b/websdk/static/js/wymeditor/iframe/default/lbl-pre.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/iframe/default/wymiframe.css b/websdk/static/js/wymeditor/iframe/default/wymiframe.css
new file mode 100644
index 0000000..769520c
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/iframe/default/wymiframe.html b/websdk/static/js/wymeditor/iframe/default/wymiframe.html
new file mode 100644
index 0000000..91a690c
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/jquery.wymeditor.explorer.js b/websdk/static/js/wymeditor/jquery.wymeditor.explorer.js
new file mode 100644
index 0000000..c177bee
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/jquery.wymeditor.js b/websdk/static/js/wymeditor/jquery.wymeditor.js
new file mode 100644
index 0000000..d7c6359
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/jquery.wymeditor.min.js b/websdk/static/js/wymeditor/jquery.wymeditor.min.js
new file mode 100644
index 0000000..b57a18e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/jquery.wymeditor.mozilla.js b/websdk/static/js/wymeditor/jquery.wymeditor.mozilla.js
new file mode 100644
index 0000000..3aa84b9
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/jquery.wymeditor.opera.js b/websdk/static/js/wymeditor/jquery.wymeditor.opera.js
new file mode 100644
index 0000000..53c2232
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/jquery.wymeditor.pack.js b/websdk/static/js/wymeditor/jquery.wymeditor.pack.js
new file mode 100644
index 0000000..427b621
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/jquery.wymeditor.safari.js b/websdk/static/js/wymeditor/jquery.wymeditor.safari.js
new file mode 100644
index 0000000..e4b9739
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/entries b/websdk/static/js/wymeditor/lang/.svn/entries
new file mode 100644
index 0000000..2f4e246
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/bg.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/bg.js.svn-base
new file mode 100644
index 0000000..576bca5
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/ca.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/ca.js.svn-base
new file mode 100644
index 0000000..c342406
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/cs.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/cs.js.svn-base
new file mode 100644
index 0000000..3939d71
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/cy.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/cy.js.svn-base
new file mode 100644
index 0000000..7d15b79
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/de.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/de.js.svn-base
new file mode 100644
index 0000000..a1e01e1
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/en.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/en.js.svn-base
new file mode 100644
index 0000000..dc7eb21
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/es.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/es.js.svn-base
new file mode 100644
index 0000000..cdb03c1
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/fa.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/fa.js.svn-base
new file mode 100644
index 0000000..9d70fcb
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/fi.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/fi.js.svn-base
new file mode 100644
index 0000000..fe1eab4
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/fr.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/fr.js.svn-base
new file mode 100644
index 0000000..9b6deb9
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/gl.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/gl.js.svn-base
new file mode 100644
index 0000000..d4786b8
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/he.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/he.js.svn-base
new file mode 100644
index 0000000..97c9675
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/hr.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/hr.js.svn-base
new file mode 100644
index 0000000..193e31a
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/hu.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/hu.js.svn-base
new file mode 100644
index 0000000..a8cdbc6
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/it.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/it.js.svn-base
new file mode 100644
index 0000000..ca632a9
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/ja.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/ja.js.svn-base
new file mode 100644
index 0000000..c9b6410
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/nb.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/nb.js.svn-base
new file mode 100644
index 0000000..7573b78
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/nl.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/nl.js.svn-base
new file mode 100644
index 0000000..cdfa21c
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/nn.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/nn.js.svn-base
new file mode 100644
index 0000000..51cec2b
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/pl.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/pl.js.svn-base
new file mode 100644
index 0000000..d6c0471
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/pt-br.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/pt-br.js.svn-base
new file mode 100644
index 0000000..2ec18fe
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/pt.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/pt.js.svn-base
new file mode 100644
index 0000000..a3d1a17
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/ru.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/ru.js.svn-base
new file mode 100644
index 0000000..7895f8d
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/sv.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/sv.js.svn-base
new file mode 100644
index 0000000..6347956
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/tr.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/tr.js.svn-base
new file mode 100644
index 0000000..d26f0ff
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/.svn/text-base/zh_cn.js.svn-base b/websdk/static/js/wymeditor/lang/.svn/text-base/zh_cn.js.svn-base
new file mode 100644
index 0000000..72f5aaf
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/bg.js b/websdk/static/js/wymeditor/lang/bg.js
new file mode 100644
index 0000000..576bca5
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/ca.js b/websdk/static/js/wymeditor/lang/ca.js
new file mode 100644
index 0000000..c342406
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/cs.js b/websdk/static/js/wymeditor/lang/cs.js
new file mode 100644
index 0000000..3939d71
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/cy.js b/websdk/static/js/wymeditor/lang/cy.js
new file mode 100644
index 0000000..7d15b79
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/de.js b/websdk/static/js/wymeditor/lang/de.js
new file mode 100644
index 0000000..a1e01e1
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/en.js b/websdk/static/js/wymeditor/lang/en.js
new file mode 100644
index 0000000..dc7eb21
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/es.js b/websdk/static/js/wymeditor/lang/es.js
new file mode 100644
index 0000000..cdb03c1
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/fa.js b/websdk/static/js/wymeditor/lang/fa.js
new file mode 100644
index 0000000..9d70fcb
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/fi.js b/websdk/static/js/wymeditor/lang/fi.js
new file mode 100644
index 0000000..fe1eab4
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/fr.js b/websdk/static/js/wymeditor/lang/fr.js
new file mode 100644
index 0000000..9b6deb9
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/gl.js b/websdk/static/js/wymeditor/lang/gl.js
new file mode 100644
index 0000000..d4786b8
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/he.js b/websdk/static/js/wymeditor/lang/he.js
new file mode 100644
index 0000000..97c9675
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/hr.js b/websdk/static/js/wymeditor/lang/hr.js
new file mode 100644
index 0000000..193e31a
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/hu.js b/websdk/static/js/wymeditor/lang/hu.js
new file mode 100644
index 0000000..a8cdbc6
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/it.js b/websdk/static/js/wymeditor/lang/it.js
new file mode 100644
index 0000000..ca632a9
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/ja.js b/websdk/static/js/wymeditor/lang/ja.js
new file mode 100644
index 0000000..c9b6410
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/nb.js b/websdk/static/js/wymeditor/lang/nb.js
new file mode 100644
index 0000000..7573b78
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/nl.js b/websdk/static/js/wymeditor/lang/nl.js
new file mode 100644
index 0000000..cdfa21c
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/nn.js b/websdk/static/js/wymeditor/lang/nn.js
new file mode 100644
index 0000000..51cec2b
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/pl.js b/websdk/static/js/wymeditor/lang/pl.js
new file mode 100644
index 0000000..d6c0471
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/pt-br.js b/websdk/static/js/wymeditor/lang/pt-br.js
new file mode 100644
index 0000000..2ec18fe
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/pt.js b/websdk/static/js/wymeditor/lang/pt.js
new file mode 100644
index 0000000..a3d1a17
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/ru.js b/websdk/static/js/wymeditor/lang/ru.js
new file mode 100644
index 0000000..7895f8d
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/sv.js b/websdk/static/js/wymeditor/lang/sv.js
new file mode 100644
index 0000000..6347956
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/tr.js b/websdk/static/js/wymeditor/lang/tr.js
new file mode 100644
index 0000000..d26f0ff
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/lang/zh_cn.js b/websdk/static/js/wymeditor/lang/zh_cn.js
new file mode 100644
index 0000000..72f5aaf
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/.svn/entries b/websdk/static/js/wymeditor/plugins/.svn/entries
new file mode 100644
index 0000000..e6a5e96
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/embed/.svn/entries b/websdk/static/js/wymeditor/plugins/embed/.svn/entries
new file mode 100644
index 0000000..20f177e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/embed/.svn/text-base/jquery.wymeditor.embed.js.svn-base b/websdk/static/js/wymeditor/plugins/embed/.svn/text-base/jquery.wymeditor.embed.js.svn-base
new file mode 100644
index 0000000..e553c3e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js b/websdk/static/js/wymeditor/plugins/embed/jquery.wymeditor.embed.js
new file mode 100644
index 0000000..e553c3e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/fullscreen/.svn/entries b/websdk/static/js/wymeditor/plugins/fullscreen/.svn/entries
new file mode 100644
index 0000000..c18dcf7
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/fullscreen/.svn/prop-base/icon_fullscreen.gif.svn-base b/websdk/static/js/wymeditor/plugins/fullscreen/.svn/prop-base/icon_fullscreen.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/fullscreen/.svn/text-base/icon_fullscreen.gif.svn-base b/websdk/static/js/wymeditor/plugins/fullscreen/.svn/text-base/icon_fullscreen.gif.svn-base
new file mode 100644
index 0000000..d2a8b0a
--- /dev/null
+++ b/websdk/static/js/wymeditor/plugins/fullscreen/.svn/text-base/icon_fullscreen.gif.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/plugins/fullscreen/.svn/text-base/jquery.wymeditor.fullscreen.js.svn-base b/websdk/static/js/wymeditor/plugins/fullscreen/.svn/text-base/jquery.wymeditor.fullscreen.js.svn-base
new file mode 100644
index 0000000..275c816
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/fullscreen/icon_fullscreen.gif b/websdk/static/js/wymeditor/plugins/fullscreen/icon_fullscreen.gif
new file mode 100644
index 0000000..d2a8b0a
--- /dev/null
+++ b/websdk/static/js/wymeditor/plugins/fullscreen/icon_fullscreen.gif
Binary files differ
diff --git a/websdk/static/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js b/websdk/static/js/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js
new file mode 100644
index 0000000..275c816
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/hovertools/.svn/entries b/websdk/static/js/wymeditor/plugins/hovertools/.svn/entries
new file mode 100644
index 0000000..dcb85a5
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/hovertools/.svn/text-base/jquery.wymeditor.hovertools.js.svn-base b/websdk/static/js/wymeditor/plugins/hovertools/.svn/text-base/jquery.wymeditor.hovertools.js.svn-base
new file mode 100644
index 0000000..2c71ba5
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js b/websdk/static/js/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js
new file mode 100644
index 0000000..2c71ba5
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/resizable/.svn/entries b/websdk/static/js/wymeditor/plugins/resizable/.svn/entries
new file mode 100644
index 0000000..8afaf68
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/resizable/.svn/text-base/jquery.wymeditor.resizable.js.svn-base b/websdk/static/js/wymeditor/plugins/resizable/.svn/text-base/jquery.wymeditor.resizable.js.svn-base
new file mode 100644
index 0000000..1ba2d2e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/resizable/.svn/text-base/readme.txt.svn-base b/websdk/static/js/wymeditor/plugins/resizable/.svn/text-base/readme.txt.svn-base
new file mode 100644
index 0000000..2a0444e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js b/websdk/static/js/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js
new file mode 100644
index 0000000..1ba2d2e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/resizable/readme.txt b/websdk/static/js/wymeditor/plugins/resizable/readme.txt
new file mode 100644
index 0000000..2a0444e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/tidy/.svn/entries b/websdk/static/js/wymeditor/plugins/tidy/.svn/entries
new file mode 100644
index 0000000..3acea50
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/tidy/.svn/prop-base/wand.png.svn-base b/websdk/static/js/wymeditor/plugins/tidy/.svn/prop-base/wand.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/README.svn-base b/websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/README.svn-base
new file mode 100644
index 0000000..acc7ffd
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/jquery.wymeditor.tidy.js.svn-base b/websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/jquery.wymeditor.tidy.js.svn-base
new file mode 100644
index 0000000..bf30c4c
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/tidy.php.svn-base b/websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/tidy.php.svn-base
new file mode 100644
index 0000000..23b9bec
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/wand.png.svn-base b/websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/wand.png.svn-base
new file mode 100644
index 0000000..bb55eea
--- /dev/null
+++ b/websdk/static/js/wymeditor/plugins/tidy/.svn/text-base/wand.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/plugins/tidy/README b/websdk/static/js/wymeditor/plugins/tidy/README
new file mode 100644
index 0000000..acc7ffd
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js b/websdk/static/js/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js
new file mode 100644
index 0000000..bf30c4c
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/tidy/tidy.php b/websdk/static/js/wymeditor/plugins/tidy/tidy.php
new file mode 100644
index 0000000..23b9bec
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/plugins/tidy/wand.png b/websdk/static/js/wymeditor/plugins/tidy/wand.png
new file mode 100644
index 0000000..bb55eea
--- /dev/null
+++ b/websdk/static/js/wymeditor/plugins/tidy/wand.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/.svn/entries b/websdk/static/js/wymeditor/skins/.svn/entries
new file mode 100644
index 0000000..81655ae
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/.svn/prop-base/wymeditor_icon.png.svn-base b/websdk/static/js/wymeditor/skins/.svn/prop-base/wymeditor_icon.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/.svn/text-base/wymeditor_icon.png.svn-base b/websdk/static/js/wymeditor/skins/.svn/text-base/wymeditor_icon.png.svn-base
new file mode 100644
index 0000000..d4fc155
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/.svn/text-base/wymeditor_icon.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/compact/.svn/entries b/websdk/static/js/wymeditor/skins/compact/.svn/entries
new file mode 100644
index 0000000..4218693
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/compact/.svn/prop-base/icons.png.svn-base b/websdk/static/js/wymeditor/skins/compact/.svn/prop-base/icons.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/compact/.svn/text-base/icons.png.svn-base b/websdk/static/js/wymeditor/skins/compact/.svn/text-base/icons.png.svn-base
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/compact/.svn/text-base/icons.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/compact/.svn/text-base/skin.css.svn-base b/websdk/static/js/wymeditor/skins/compact/.svn/text-base/skin.css.svn-base
new file mode 100644
index 0000000..4a6a0c6
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/compact/.svn/text-base/skin.js.svn-base b/websdk/static/js/wymeditor/skins/compact/.svn/text-base/skin.js.svn-base
new file mode 100644
index 0000000..cfb7cc1
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/compact/icons.png b/websdk/static/js/wymeditor/skins/compact/icons.png
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/compact/icons.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/compact/skin.css b/websdk/static/js/wymeditor/skins/compact/skin.css
new file mode 100644
index 0000000..4a6a0c6
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/compact/skin.js b/websdk/static/js/wymeditor/skins/compact/skin.js
new file mode 100644
index 0000000..cfb7cc1
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/default/.svn/entries b/websdk/static/js/wymeditor/skins/default/.svn/entries
new file mode 100644
index 0000000..3893f6d
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/default/.svn/prop-base/icons.png.svn-base b/websdk/static/js/wymeditor/skins/default/.svn/prop-base/icons.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/default/.svn/text-base/icons.png.svn-base b/websdk/static/js/wymeditor/skins/default/.svn/text-base/icons.png.svn-base
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/default/.svn/text-base/icons.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/default/.svn/text-base/skin.css.svn-base b/websdk/static/js/wymeditor/skins/default/.svn/text-base/skin.css.svn-base
new file mode 100644
index 0000000..eb4680f
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/default/.svn/text-base/skin.js.svn-base b/websdk/static/js/wymeditor/skins/default/.svn/text-base/skin.js.svn-base
new file mode 100644
index 0000000..5f6d97e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/default/icons.png b/websdk/static/js/wymeditor/skins/default/icons.png
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/default/icons.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/default/skin.css b/websdk/static/js/wymeditor/skins/default/skin.css
new file mode 100644
index 0000000..eb4680f
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/default/skin.js b/websdk/static/js/wymeditor/skins/default/skin.js
new file mode 100644
index 0000000..5f6d97e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/minimal/.svn/entries b/websdk/static/js/wymeditor/skins/minimal/.svn/entries
new file mode 100644
index 0000000..fb50d46
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/minimal/.svn/text-base/skin.css.svn-base b/websdk/static/js/wymeditor/skins/minimal/.svn/text-base/skin.css.svn-base
new file mode 100644
index 0000000..cea8d84
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/minimal/.svn/text-base/skin.js.svn-base b/websdk/static/js/wymeditor/skins/minimal/.svn/text-base/skin.js.svn-base
new file mode 100644
index 0000000..af29ed4
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/minimal/images/.svn/entries b/websdk/static/js/wymeditor/skins/minimal/images/.svn/entries
new file mode 100644
index 0000000..ff30362
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.header.gif.svn-base b/websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.header.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.selector.silver.gif.svn-base b/websdk/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/websdk/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/websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.wymeditor.png.svn-base b/websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/bg.wymeditor.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/icons.silver.gif.svn-base b/websdk/static/js/wymeditor/skins/minimal/images/.svn/prop-base/icons.silver.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.header.gif.svn-base b/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.header.gif.svn-base
new file mode 100644
index 0000000..b2d2907
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.header.gif.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.selector.silver.gif.svn-base b/websdk/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/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.selector.silver.gif.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.wymeditor.png.svn-base b/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.wymeditor.png.svn-base
new file mode 100644
index 0000000..1e84813
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/bg.wymeditor.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/icons.silver.gif.svn-base b/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/icons.silver.gif.svn-base
new file mode 100644
index 0000000..8c6a4fb
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/minimal/images/.svn/text-base/icons.silver.gif.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/minimal/images/bg.header.gif b/websdk/static/js/wymeditor/skins/minimal/images/bg.header.gif
new file mode 100644
index 0000000..b2d2907
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/minimal/images/bg.header.gif
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/minimal/images/bg.selector.silver.gif b/websdk/static/js/wymeditor/skins/minimal/images/bg.selector.silver.gif
new file mode 100644
index 0000000..e65976b
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/minimal/images/bg.selector.silver.gif
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/minimal/images/bg.wymeditor.png b/websdk/static/js/wymeditor/skins/minimal/images/bg.wymeditor.png
new file mode 100644
index 0000000..1e84813
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/minimal/images/bg.wymeditor.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/minimal/images/icons.silver.gif b/websdk/static/js/wymeditor/skins/minimal/images/icons.silver.gif
new file mode 100644
index 0000000..8c6a4fb
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/minimal/images/icons.silver.gif
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/minimal/skin.css b/websdk/static/js/wymeditor/skins/minimal/skin.css
new file mode 100644
index 0000000..cea8d84
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/minimal/skin.js b/websdk/static/js/wymeditor/skins/minimal/skin.js
new file mode 100644
index 0000000..af29ed4
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/.svn/entries b/websdk/static/js/wymeditor/skins/silver/.svn/entries
new file mode 100644
index 0000000..2969c6b
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/.svn/prop-base/COPYING.svn-base b/websdk/static/js/wymeditor/skins/silver/.svn/prop-base/COPYING.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/.svn/prop-base/README.svn-base b/websdk/static/js/wymeditor/skins/silver/.svn/prop-base/README.svn-base
new file mode 100644
index 0000000..869ac71
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/.svn/text-base/COPYING.svn-base b/websdk/static/js/wymeditor/skins/silver/.svn/text-base/COPYING.svn-base
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/.svn/text-base/README.svn-base b/websdk/static/js/wymeditor/skins/silver/.svn/text-base/README.svn-base
new file mode 100644
index 0000000..130dc46
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/.svn/text-base/skin.css.svn-base b/websdk/static/js/wymeditor/skins/silver/.svn/text-base/skin.css.svn-base
new file mode 100644
index 0000000..8284d81
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/.svn/text-base/skin.js.svn-base b/websdk/static/js/wymeditor/skins/silver/.svn/text-base/skin.js.svn-base
new file mode 100644
index 0000000..948ed91
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/COPYING b/websdk/static/js/wymeditor/skins/silver/COPYING
new file mode 100755
index 0000000..94a9ed0
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/README b/websdk/static/js/wymeditor/skins/silver/README
new file mode 100755
index 0000000..130dc46
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/images/.svn/entries b/websdk/static/js/wymeditor/skins/silver/images/.svn/entries
new file mode 100644
index 0000000..d70fd13
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.header.gif.svn-base b/websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.header.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.selector.silver.gif.svn-base b/websdk/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/websdk/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/websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.wymeditor.png.svn-base b/websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/bg.wymeditor.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/icons.silver.gif.svn-base b/websdk/static/js/wymeditor/skins/silver/images/.svn/prop-base/icons.silver.gif.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.header.gif.svn-base b/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.header.gif.svn-base
new file mode 100644
index 0000000..b2d2907
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.header.gif.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.selector.silver.gif.svn-base b/websdk/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/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.selector.silver.gif.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.wymeditor.png.svn-base b/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.wymeditor.png.svn-base
new file mode 100644
index 0000000..1e84813
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/bg.wymeditor.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/icons.silver.gif.svn-base b/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/icons.silver.gif.svn-base
new file mode 100644
index 0000000..8c6a4fb
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/silver/images/.svn/text-base/icons.silver.gif.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/silver/images/bg.header.gif b/websdk/static/js/wymeditor/skins/silver/images/bg.header.gif
new file mode 100644
index 0000000..b2d2907
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/silver/images/bg.header.gif
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/silver/images/bg.selector.silver.gif b/websdk/static/js/wymeditor/skins/silver/images/bg.selector.silver.gif
new file mode 100644
index 0000000..e65976b
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/silver/images/bg.selector.silver.gif
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/silver/images/bg.wymeditor.png b/websdk/static/js/wymeditor/skins/silver/images/bg.wymeditor.png
new file mode 100644
index 0000000..1e84813
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/silver/images/bg.wymeditor.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/silver/images/icons.silver.gif b/websdk/static/js/wymeditor/skins/silver/images/icons.silver.gif
new file mode 100644
index 0000000..8c6a4fb
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/silver/images/icons.silver.gif
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/silver/skin.css b/websdk/static/js/wymeditor/skins/silver/skin.css
new file mode 100644
index 0000000..56e3248
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/silver/skin.js b/websdk/static/js/wymeditor/skins/silver/skin.js
new file mode 100644
index 0000000..948ed91
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/twopanels/.svn/entries b/websdk/static/js/wymeditor/skins/twopanels/.svn/entries
new file mode 100644
index 0000000..a477f85
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/twopanels/.svn/prop-base/icons.png.svn-base b/websdk/static/js/wymeditor/skins/twopanels/.svn/prop-base/icons.png.svn-base
new file mode 100644
index 0000000..5e9587e
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/twopanels/.svn/text-base/icons.png.svn-base b/websdk/static/js/wymeditor/skins/twopanels/.svn/text-base/icons.png.svn-base
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/twopanels/.svn/text-base/icons.png.svn-base
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.css.svn-base b/websdk/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.css.svn-base
new file mode 100644
index 0000000..7e6b8fd
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.js.svn-base b/websdk/static/js/wymeditor/skins/twopanels/.svn/text-base/skin.js.svn-base
new file mode 100644
index 0000000..e82efc5
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/twopanels/icons.png b/websdk/static/js/wymeditor/skins/twopanels/icons.png
new file mode 100644
index 0000000..c6eb463
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/twopanels/icons.png
Binary files differ
diff --git a/websdk/static/js/wymeditor/skins/twopanels/skin.css b/websdk/static/js/wymeditor/skins/twopanels/skin.css
new file mode 100644
index 0000000..7e6b8fd
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/twopanels/skin.js b/websdk/static/js/wymeditor/skins/twopanels/skin.js
new file mode 100644
index 0000000..e82efc5
--- /dev/null
+++ b/websdk/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/websdk/static/js/wymeditor/skins/wymeditor_icon.png b/websdk/static/js/wymeditor/skins/wymeditor_icon.png
new file mode 100644
index 0000000..d4fc155
--- /dev/null
+++ b/websdk/static/js/wymeditor/skins/wymeditor_icon.png
Binary files differ
diff --git a/websdk/static/static b/websdk/static/static
new file mode 120000
index 0000000..945c9b4
--- /dev/null
+++ b/websdk/static/static
@@ -0,0 +1 @@
+. \ No newline at end of file
diff --git a/websdk/studio.py b/websdk/studio.py
new file mode 100644
index 0000000..d7b5879
--- /dev/null
+++ b/websdk/studio.py
@@ -0,0 +1,105 @@
+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()
+
+def list_files(directory):
+ files=os.listdir(directory)
+ print "showing %s" % directory
+ return files
+
+@app.route('/')
+def index():
+ return render_response('index.html')
+
+@app.route('/edit/')
+@app.route('/edit/<path:filename>')
+def edit(filename="activity.py"):
+ icon = 'document-generic.svg'
+ mode = ''
+ if filename.endswith('.py'):
+ icon = 'text-x-python.svg'
+ mode = 'python'
+ if filename.endswith('.html'):
+ icon = 'text-uri-list.svg'
+ mode = 'html'
+ if filename.endswith('.css'):
+ icon = 'text-uri-list.svg'
+ mode = 'css'
+ if filename.endswith('.js'):
+ icon = 'text-uri-list.svg'
+ mode = 'javascript'
+ 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))
+
+@app.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))
+
+@app.route('/files/')
+@app.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.svg',
+ 'href': '/files/%s' % os.path.join(directory,"..") })
+ for filename in sorted(filelist):
+ fullname = os.path.join(directory,filename)
+ icon = 'document-generic.svg'
+ href = '/edit/%s/%s' % (directory,filename)
+ if filename.endswith('.py'):
+ icon = 'text-x-python.svg'
+ if filename.endswith('.html'):
+ icon = 'text-uri-list.svg'
+ if filename.endswith('.css'):
+ icon = 'text-uri-list.svg'
+ if filename.endswith('.js'):
+ icon = 'text-uri-list.svg'
+ if os.path.isdir(fullname):
+ icon = 'folder.svg'
+ href = '/files/%s' % fullname
+ if filename.endswith('.xo'):
+ href = '#'
+ if filename.startswith('.'):
+ continue
+ if filename.endswith('.pyc'):
+ continue
+ files.append( { 'name': filename,
+ 'icon': icon,
+ 'href': href } )
+ return render_response('filer.html', dict(files=files, absdir=os.path.normpath(directory)))
+
+@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')
diff --git a/websdk/templates/editor.html b/websdk/templates/editor.html
new file mode 100644
index 0000000..7fb4737
--- /dev/null
+++ b/websdk/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.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>
+<!-- <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 directory" 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 to main" 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/websdk/templates/filer.html b/websdk/templates/filer.html
new file mode 100644
index 0000000..eb2a003
--- /dev/null
+++ b/websdk/templates/filer.html
@@ -0,0 +1,44 @@
+<!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" />
+<script src="/static/js/jquery-1.6.2.js" type="text/javascript"></script>
+<script src="/static/js/jquery.corner.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> -->
+</head>
+<body>
+ <div id="filer-header">
+ <span>${absdir}/</span>
+ </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}" />
+ <span class="moz-file"><object type="image/svg+xml"
+ data="/static/icons/${file.icon}">
+ </object></span>
+ <br/>${file.name}
+ </a></li>
+ </ul>
+ </div>
+<script>
+window.onload = function() {
+ $('.file').corner("15px");
+ $('.bling').show("slow");
+ $('filer-header').slideDown("slow");
+ if ($.browser.mozilla) {
+ $('#filer img').hide();
+ $('#filer span').show();
+ }
+};
+</script>
+</body>
+</html>
diff --git a/websdk/templates/index.html b/websdk/templates/index.html
new file mode 100644
index 0000000..3b655dc
--- /dev/null
+++ b/websdk/templates/index.html
@@ -0,0 +1,52 @@
+<!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">
+ <header><h1>Sugar WebSDK <span id="beta">beta</span></h1></header>
+ <section>
+ <article><h2>Technology Preview</h2></article>
+ <p>The following are examples of things that can be achieved with WebSDK.</p>
+ <ul>
+ <li><input type="button" class="btn"
+ value="Webpy: WebSDK Studio" onclick='location.href="/edit?filename=webpy.py&amp;directory=websdk"' />
+ - <input type="button" class="btn"
+ value="Split view" onclick='location.href="/vsplit"' />
+ <p class="subtitle">Featuring Ace Editor and some tricks.</p>
+ </li>
+ <li><input type="button" class="btn"
+ value="Webpy bundle browser" onclick='location.href="/files"' />
+ <p class="subtitle"><i>"Write less, do more".</i> Jquery is a bag of candy.</p>
+ </li>
+ <li><input type="button" class="btn"
+ value="Call root python controller" onclick='location.href="/"' />
+ </li>
+ <li><input type="button" class="btn"
+ value="Sugar Integration: Journal" onclick='location.href="/journal"' />
+<input type="button" class="btn"
+ value="Sugar Integration: Collaboration" onclick='location.href="/collaboration"' />
+ <p class="subtitle">Not yet implemented.</p>
+ </li>
+ <li><input type="button" class="btn"
+ value="Debugging tools." onclick='location.href="/debug"' />
+ <p class="subtitle">Begin with right click, inspect element.</p>
+ </li>
+ </ul>
+ </section>
+ </div>
+ <script>
+ window.onload = function() {
+ $('#content').fadeIn(700);
+ $('#beta').delay(1500).fadeIn("slow");
+ }
+ </script>
+</body>
+</html>
diff --git a/websdk/templates/skel.html b/websdk/templates/skel.html
new file mode 100644
index 0000000..65d351e
--- /dev/null
+++ b/websdk/templates/skel.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>Filer</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>
+window.onload = function() {
+};
+</script>
+</body>
+</html>
diff --git a/websdk/templates/split-view.html b/websdk/templates/split-view.html
new file mode 100644
index 0000000..e23b696
--- /dev/null
+++ b/websdk/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/websdk/templates/wysiwyg-editor.html b/websdk/templates/wysiwyg-editor.html
new file mode 100644
index 0000000..3f162c9
--- /dev/null
+++ b/websdk/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/webkit_local/__init__.py b/websdk/webkit_local/__init__.py
new file mode 100644
index 0000000..e309204
--- /dev/null
+++ b/websdk/webkit_local/__init__.py
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+import webkit_local
+
+from webkit_local import *
diff --git a/websdk/webkit_local/libWebKitGdk.so.1 b/websdk/webkit_local/libWebKitGdk.so.1
new file mode 100644
index 0000000..b2c1f0b
--- /dev/null
+++ b/websdk/webkit_local/libWebKitGdk.so.1
Binary files differ
diff --git a/websdk/webkit_local/libxslt.so.1 b/websdk/webkit_local/libxslt.so.1
new file mode 100644
index 0000000..4dc90cd
--- /dev/null
+++ b/websdk/webkit_local/libxslt.so.1
Binary files differ
diff --git a/websdk/webkit_local/webkit.so b/websdk/webkit_local/webkit.so
new file mode 100644
index 0000000..bc94300
--- /dev/null
+++ b/websdk/webkit_local/webkit.so
Binary files differ
diff --git a/websdk/webpy.py b/websdk/webpy.py
new file mode 100644
index 0000000..428df6f
--- /dev/null
+++ b/websdk/webpy.py
@@ -0,0 +1,174 @@
+#!/bin/env python
+# -*- coding: UTF-8 -*-
+# this file is deprecated in favor of studio.py
+# left to implement couple of details to remove
+
+import os
+import os.path
+import sys
+try:
+ import cherrypy
+except ImportError:
+ import cherrypy_local as cherrypy
+from genshi.template import TemplateLoader
+
+class Root(object):
+
+ def __init__(self, data):
+ self.data = data
+ try:
+ self.bundle_dir = data['bundle_dir']
+ except KeyError:
+ self.bundle_dir = os.curdir
+ self.loader = TemplateLoader(
+ os.path.join(self.bundle_dir, 'templates'),
+ auto_reload=True)
+
+ @cherrypy.expose
+ def index(self):
+ port = cherrypy.config['server.socket_port']
+ return '''Server is running on port %s. <br/>
+ Try pointing a browser at
+ <a href="http://localhost:%s/www/index.html">http://localhost:%s/www/index.html</a>''' % (port, port, port)
+
+ @cherrypy.expose
+ def debug(self):
+ port = cherrypy.config['server.socket_port']
+ return '''Try right clicking on any element on the main canvas and choosing "Inspect Element".
+ <br /><a href="http://localhost:%s/www/index.html">Return to main</a>''' % (port)
+
+ @cherrypy.expose
+ def candy(self):
+ return '''Not yet implemented.
+ <br /><a href="http://localhost:%s/www/index.html">Return to main</a>''' % (port)
+
+ @cherrypy.expose
+ def journal(self):
+ port = cherrypy.config['server.socket_port']
+ return '''Not yet implemented.
+ <br /><a href="http://localhost:%s/www/index.html">Return to main</a>''' % (port)
+
+ @cherrypy.expose
+ def collaboration(self):
+ port = cherrypy.config['server.socket_port']
+ return '''Not yet implemented.
+ <br /><a href="http://localhost:%s/www/index.html">Return to main</a>''' % (port)
+
+ def list_files(self, directory):
+ files=os.listdir(directory)
+ print "showing %s" % directory
+ return files
+
+ @cherrypy.expose
+ def browse(self, directory="."):
+ filelist = self.list_files(directory)
+ files = []
+ if not os.path.abspath(directory)==os.path.abspath("."):
+ files.append( { 'name': '..',
+ 'icon': 'folder.svg',
+ 'href': 'browse?directory=%s' % os.path.join(directory,"..") })
+ for filename in sorted(filelist):
+ fullname = os.path.join(directory,filename)
+ icon = 'document-generic.svg'
+ href = 'edit?filename=%s&directory=%s' % (fullname,directory)
+ if filename.endswith('.py'):
+ icon = 'text-x-python.svg'
+ if filename.endswith('.html'):
+ icon = 'text-uri-list.svg'
+ if filename.endswith('.css'):
+ icon = 'text-uri-list.svg'
+ if filename.endswith('.js'):
+ icon = 'text-uri-list.svg'
+ if os.path.isdir(fullname):
+ icon = 'folder.svg'
+ href = 'browse?directory=%s' % fullname
+ if filename.endswith('.xo'):
+ href = '#'
+ if filename.startswith('.'):
+ continue
+ if filename.endswith('.pyc'):
+ continue
+ files.append( { 'name': filename,
+ 'icon': icon,
+ 'href': href } )
+
+ tmpl = self.loader.load('filer.html')
+ return tmpl.generate(files=files, absdir=os.path.normpath(directory)
+ ).render('html', doctype='html')
+ @cherrypy.expose
+ def vsplit(self, frame1="/browse", frame2="/browse"):
+ tmpl = self.loader.load('split-view.html')
+ return tmpl.generate(frame1=frame1, frame2=frame2
+ ).render('html', doctype='html')
+
+
+ @cherrypy.expose
+ def edit(self, directory=".", filename="activity.py", editor="ace"):
+ icon = 'document-generic.svg'
+ mode = ''
+ if filename.endswith('.py'):
+ icon = 'text-x-python.svg'
+ mode = 'python'
+ if filename.endswith('.html'):
+ icon = 'text-uri-list.svg'
+ mode = 'html'
+ if filename.endswith('.css'):
+ icon = 'text-uri-list.svg'
+ mode = 'css'
+ if filename.endswith('.js'):
+ icon = 'text-uri-list.svg'
+ mode = 'javascript'
+ content = open(filename).read().decode('utf-8')
+ if editor=="wysiwyg":
+ tmpl = self.loader.load('wysiwyg-editor.html')
+ else:
+ tmpl = self.loader.load('editor.html')
+ return tmpl.generate(content=content, icon=icon,basename=os.path.basename(filename),
+ filename=filename, directory=directory, absdir=os.path.normpath(directory),
+ mode=mode).render('html', doctype='html', encoding='utf-8')
+
+ @cherrypy.expose
+ def save(self, filename, content, directory):
+ f=open(filename,"wb")
+ content = content.replace('\r\n', '\n').replace('\r', '\n') # HACK
+ f.write(content)
+ print "saving content: %s" % filename
+ f.close()
+ href = "/browse?directory=%s" % directory
+ cherrypy.tools.redirect.callable(url=href, internal=False)
+ return "content saved: %s" % content
+
+ @cherrypy.expose
+ def delete(self, filename):
+ os.unlink(filename)
+ cherrypy.tools.redirect.callable(url='/browse', internal=True)
+
+def start(root, port):
+ root = os.path.abspath(root)
+ print "root is %s" % root
+ data = {}
+ # Some global configuration; note that this could be moved into a
+ # configuration file
+ cherrypy.config.update({
+ 'server.socket_port': port,
+ 'tools.encode.on': True, 'tools.encode.encoding': 'utf-8',
+ 'tools.decode.on': True,
+ 'tools.trailing_slash.on': True,
+ 'tools.staticdir.root': root,
+ })
+
+ cherrypy.quickstart(Root(data), '/', {
+ '/www': {
+ 'tools.staticdir.on': True,
+ 'tools.staticdir.dir': 'www'
+ }
+ })
+ #cherrypy.tree.mount(Root({}), '/', {
+ # '/www': {
+ # 'tools.staticdir.on': True,
+ # 'tools.staticdir.dir': 'www'
+ #})
+ #cherrypy.engine.start()
+
+if __name__ == '__main__':
+ start(os.path.abspath(os.curdir), int(sys.argv[1]))
diff --git a/websdk/werkzeug/__init__.py b/websdk/werkzeug/__init__.py
new file mode 100644
index 0000000..fea2c98
--- /dev/null
+++ b/websdk/werkzeug/__init__.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug
+ ~~~~~~~~
+
+ Werkzeug is the Swiss Army knife of Python web development.
+
+ It provides useful classes and functions for any WSGI application to make
+ the life of a python web developer much easier. All of the provided
+ classes are independent from each other so you can mix it with any other
+ library.
+
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+from types import ModuleType
+import sys
+
+
+# the version. Usually set automatically by a script.
+__version__ = '0.8-dev'
+
+
+# This import magic raises concerns quite often which is why the implementation
+# and motivation is explained here in detail now.
+#
+# The majority of the functions and classes provided by Werkzeug work on the
+# HTTP and WSGI layer. There is no useful grouping for those which is why
+# they are all importable from "werkzeug" instead of the modules where they are
+# implemented. The downside of that is, that now everything would be loaded at
+# once, even if unused.
+#
+# The implementation of a lazy-loading module in this file replaces the
+# werkzeug package when imported from within. Attribute access to the werkzeug
+# module will then lazily import from the modules that implement the objects.
+
+
+# import mapping to objects in other modules
+all_by_module = {
+ 'werkzeug.debug': ['DebuggedApplication'],
+ 'werkzeug.local': ['Local', 'LocalManager', 'LocalProxy',
+ 'LocalStack', 'release_local'],
+ 'werkzeug.templates': ['Template'],
+ 'werkzeug.serving': ['run_simple'],
+ 'werkzeug.test': ['Client', 'EnvironBuilder', 'create_environ',
+ 'run_wsgi_app'],
+ 'werkzeug.testapp': ['test_app'],
+ 'werkzeug.exceptions': ['abort', 'Aborter'],
+ 'werkzeug.urls': ['url_decode', 'url_encode', 'url_quote',
+ 'url_quote_plus', 'url_unquote',
+ 'url_unquote_plus', 'url_fix', 'Href',
+ 'iri_to_uri', 'uri_to_iri'],
+ 'werkzeug.formparser': ['parse_form_data'],
+ 'werkzeug.utils': ['escape', 'environ_property',
+ 'append_slash_redirect', 'redirect',
+ 'cached_property', 'import_string',
+ 'dump_cookie', 'parse_cookie', 'unescape',
+ 'format_string', 'find_modules', 'header_property',
+ 'html', 'xhtml', 'HTMLBuilder',
+ 'validate_arguments', 'ArgumentValidationError',
+ 'bind_arguments', 'secure_filename'],
+ 'werkzeug.wsgi': ['get_current_url', 'get_host', 'pop_path_info',
+ 'peek_path_info', 'SharedDataMiddleware',
+ 'DispatcherMiddleware', 'ClosingIterator',
+ 'FileWrapper', 'make_line_iter', 'LimitedStream',
+ 'responder', 'wrap_file', 'extract_path_info'],
+ 'werkzeug.datastructures': ['MultiDict', 'CombinedMultiDict', 'Headers',
+ 'EnvironHeaders', 'ImmutableList',
+ 'ImmutableDict', 'ImmutableMultiDict',
+ 'TypeConversionDict', 'ImmutableTypeConversionDict',
+ 'Accept', 'MIMEAccept', 'CharsetAccept',
+ 'LanguageAccept', 'RequestCacheControl',
+ 'ResponseCacheControl', 'ETags', 'HeaderSet',
+ 'WWWAuthenticate', 'Authorization',
+ 'FileMultiDict', 'CallbackDict', 'FileStorage',
+ 'OrderedMultiDict', 'ImmutableOrderedMultiDict'],
+ 'werkzeug.useragents': ['UserAgent'],
+ 'werkzeug.http': ['parse_etags', 'parse_date', 'http_date',
+ 'cookie_date', 'parse_cache_control_header',
+ 'is_resource_modified', 'parse_accept_header',
+ 'parse_set_header', 'quote_etag', 'unquote_etag',
+ 'generate_etag', 'dump_header',
+ 'parse_list_header', 'parse_dict_header',
+ 'parse_authorization_header',
+ 'parse_www_authenticate_header',
+ 'remove_entity_headers', 'is_entity_header',
+ 'remove_hop_by_hop_headers', 'parse_options_header',
+ 'dump_options_header', 'is_hop_by_hop_header',
+ 'unquote_header_value',
+ 'quote_header_value', 'HTTP_STATUS_CODES'],
+ 'werkzeug.wrappers': ['BaseResponse', 'BaseRequest', 'Request',
+ 'Response', 'AcceptMixin', 'ETagRequestMixin',
+ 'ETagResponseMixin', 'ResponseStreamMixin',
+ 'CommonResponseDescriptorsMixin',
+ 'UserAgentMixin', 'AuthorizationMixin',
+ 'WWWAuthenticateMixin',
+ 'CommonRequestDescriptorsMixin'],
+ 'werkzeug.security': ['generate_password_hash', 'check_password_hash'],
+ # the undocumented easteregg ;-)
+ 'werkzeug._internal': ['_easteregg']
+}
+
+# modules that should be imported when accessed as attributes of werkzeug
+attribute_modules = frozenset(['exceptions', 'routing', 'script'])
+
+
+object_origins = {}
+for module, items in all_by_module.iteritems():
+ for item in items:
+ object_origins[item] = module
+
+
+class module(ModuleType):
+ """Automatically import objects from the modules."""
+
+ def __getattr__(self, name):
+ if name in object_origins:
+ module = __import__(object_origins[name], None, None, [name])
+ for extra_name in all_by_module[module.__name__]:
+ setattr(self, extra_name, getattr(module, extra_name))
+ return getattr(module, name)
+ elif name in attribute_modules:
+ __import__('werkzeug.' + name)
+ return ModuleType.__getattribute__(self, name)
+
+ def __dir__(self):
+ """Just show what we want to show."""
+ result = list(new_module.__all__)
+ result.extend(('__file__', '__path__', '__doc__', '__all__',
+ '__docformat__', '__name__', '__path__',
+ '__package__', '__version__'))
+ return result
+
+# keep a reference to this module so that it's not garbage collected
+old_module = sys.modules['werkzeug']
+
+
+# setup the new module and patch it into the dict of loaded modules
+new_module = sys.modules['werkzeug'] = module('werkzeug')
+new_module.__dict__.update({
+ '__file__': __file__,
+ '__package__': 'werkzeug',
+ '__path__': __path__,
+ '__doc__': __doc__,
+ '__version__': __version__,
+ '__all__': tuple(object_origins) + tuple(attribute_modules),
+ '__docformat__': 'restructuredtext en'
+})
diff --git a/websdk/werkzeug/_internal.py b/websdk/werkzeug/_internal.py
new file mode 100644
index 0000000..f148f14
--- /dev/null
+++ b/websdk/werkzeug/_internal.py
@@ -0,0 +1,405 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug._internal
+ ~~~~~~~~~~~~~~~~~~
+
+ This module provides internally used helpers and constants.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import inspect
+from weakref import WeakKeyDictionary
+from cStringIO import StringIO
+from Cookie import SimpleCookie, Morsel, CookieError
+from time import gmtime
+from datetime import datetime, date
+
+
+_logger = None
+_empty_stream = StringIO('')
+_signature_cache = WeakKeyDictionary()
+_epoch_ord = date(1970, 1, 1).toordinal()
+
+
+HTTP_STATUS_CODES = {
+ 100: 'Continue',
+ 101: 'Switching Protocols',
+ 102: 'Processing',
+ 200: 'OK',
+ 201: 'Created',
+ 202: 'Accepted',
+ 203: 'Non Authoritative Information',
+ 204: 'No Content',
+ 205: 'Reset Content',
+ 206: 'Partial Content',
+ 207: 'Multi Status',
+ 226: 'IM Used', # see RFC 3229
+ 300: 'Multiple Choices',
+ 301: 'Moved Permanently',
+ 302: 'Found',
+ 303: 'See Other',
+ 304: 'Not Modified',
+ 305: 'Use Proxy',
+ 307: 'Temporary Redirect',
+ 400: 'Bad Request',
+ 401: 'Unauthorized',
+ 402: 'Payment Required', # unused
+ 403: 'Forbidden',
+ 404: 'Not Found',
+ 405: 'Method Not Allowed',
+ 406: 'Not Acceptable',
+ 407: 'Proxy Authentication Required',
+ 408: 'Request Timeout',
+ 409: 'Conflict',
+ 410: 'Gone',
+ 411: 'Length Required',
+ 412: 'Precondition Failed',
+ 413: 'Request Entity Too Large',
+ 414: 'Request URI Too Long',
+ 415: 'Unsupported Media Type',
+ 416: 'Requested Range Not Satisfiable',
+ 417: 'Expectation Failed',
+ 418: 'I\'m a teapot', # see RFC 2324
+ 422: 'Unprocessable Entity',
+ 423: 'Locked',
+ 424: 'Failed Dependency',
+ 426: 'Upgrade Required',
+ 449: 'Retry With', # proprietary MS extension
+ 500: 'Internal Server Error',
+ 501: 'Not Implemented',
+ 502: 'Bad Gateway',
+ 503: 'Service Unavailable',
+ 504: 'Gateway Timeout',
+ 505: 'HTTP Version Not Supported',
+ 507: 'Insufficient Storage',
+ 510: 'Not Extended'
+}
+
+
+class _Missing(object):
+
+ def __repr__(self):
+ return 'no value'
+
+ def __reduce__(self):
+ return '_missing'
+
+_missing = _Missing()
+
+
+def _proxy_repr(cls):
+ def proxy_repr(self):
+ return '%s(%s)' % (self.__class__.__name__, cls.__repr__(self))
+ return proxy_repr
+
+
+def _get_environ(obj):
+ env = getattr(obj, 'environ', obj)
+ assert isinstance(env, dict), \
+ '%r is not a WSGI environment (has to be a dict)' % type(obj).__name__
+ return env
+
+
+def _log(type, message, *args, **kwargs):
+ """Log into the internal werkzeug logger."""
+ global _logger
+ if _logger is None:
+ import logging
+ _logger = logging.getLogger('werkzeug')
+ # Only set up a default log handler if the
+ # end-user application didn't set anything up.
+ if not logging.root.handlers and _logger.level == logging.NOTSET:
+ _logger.setLevel(logging.INFO)
+ handler = logging.StreamHandler()
+ _logger.addHandler(handler)
+ getattr(_logger, type)(message.rstrip(), *args, **kwargs)
+
+
+def _parse_signature(func):
+ """Return a signature object for the function."""
+ if hasattr(func, 'im_func'):
+ func = func.im_func
+
+ # if we have a cached validator for this function, return it
+ parse = _signature_cache.get(func)
+ if parse is not None:
+ return parse
+
+ # inspect the function signature and collect all the information
+ positional, vararg_var, kwarg_var, defaults = inspect.getargspec(func)
+ defaults = defaults or ()
+ arg_count = len(positional)
+ arguments = []
+ for idx, name in enumerate(positional):
+ if isinstance(name, list):
+ raise TypeError('cannot parse functions that unpack tuples '
+ 'in the function signature')
+ try:
+ default = defaults[idx - arg_count]
+ except IndexError:
+ param = (name, False, None)
+ else:
+ param = (name, True, default)
+ arguments.append(param)
+ arguments = tuple(arguments)
+
+ def parse(args, kwargs):
+ new_args = []
+ missing = []
+ extra = {}
+
+ # consume as many arguments as positional as possible
+ for idx, (name, has_default, default) in enumerate(arguments):
+ try:
+ new_args.append(args[idx])
+ except IndexError:
+ try:
+ new_args.append(kwargs.pop(name))
+ except KeyError:
+ if has_default:
+ new_args.append(default)
+ else:
+ missing.append(name)
+ else:
+ if name in kwargs:
+ extra[name] = kwargs.pop(name)
+
+ # handle extra arguments
+ extra_positional = args[arg_count:]
+ if vararg_var is not None:
+ new_args.extend(extra_positional)
+ extra_positional = ()
+ if kwargs and not kwarg_var is not None:
+ extra.update(kwargs)
+ kwargs = {}
+
+ return new_args, kwargs, missing, extra, extra_positional, \
+ arguments, vararg_var, kwarg_var
+ _signature_cache[func] = parse
+ return parse
+
+
+def _patch_wrapper(old, new):
+ """Helper function that forwards all the function details to the
+ decorated function."""
+ try:
+ new.__name__ = old.__name__
+ new.__module__ = old.__module__
+ new.__doc__ = old.__doc__
+ new.__dict__ = old.__dict__
+ except Exception:
+ pass
+ return new
+
+
+def _decode_unicode(value, charset, errors):
+ """Like the regular decode function but this one raises an
+ `HTTPUnicodeError` if errors is `strict`."""
+ fallback = None
+ if errors.startswith('fallback:'):
+ fallback = errors[9:]
+ errors = 'strict'
+ try:
+ return value.decode(charset, errors)
+ except UnicodeError, e:
+ if fallback is not None:
+ return value.decode(fallback, 'replace')
+ from werkzeug.exceptions import HTTPUnicodeError
+ raise HTTPUnicodeError(str(e))
+
+
+def _iter_modules(path):
+ """Iterate over all modules in a package."""
+ import os
+ import pkgutil
+ if hasattr(pkgutil, 'iter_modules'):
+ for importer, modname, ispkg in pkgutil.iter_modules(path):
+ yield modname, ispkg
+ return
+ from inspect import getmodulename
+ from pydoc import ispackage
+ found = set()
+ for path in path:
+ for filename in os.listdir(path):
+ p = os.path.join(path, filename)
+ modname = getmodulename(filename)
+ if modname and modname != '__init__':
+ if modname not in found:
+ found.add(modname)
+ yield modname, ispackage(modname)
+
+
+def _dump_date(d, delim):
+ """Used for `http_date` and `cookie_date`."""
+ if d is None:
+ d = gmtime()
+ elif isinstance(d, datetime):
+ d = d.utctimetuple()
+ elif isinstance(d, (int, long, float)):
+ d = gmtime(d)
+ return '%s, %02d%s%s%s%s %02d:%02d:%02d GMT' % (
+ ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')[d.tm_wday],
+ d.tm_mday, delim,
+ ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
+ 'Oct', 'Nov', 'Dec')[d.tm_mon - 1],
+ delim, str(d.tm_year), d.tm_hour, d.tm_min, d.tm_sec
+ )
+
+
+def _date_to_unix(arg):
+ """Converts a timetuple, integer or datetime object into the seconds from
+ epoch in utc.
+ """
+ if isinstance(arg, datetime):
+ arg = arg.utctimetuple()
+ elif isinstance(arg, (int, long, float)):
+ return int(arg)
+ year, month, day, hour, minute, second = arg[:6]
+ days = date(year, month, 1).toordinal() - _epoch_ord + day - 1
+ hours = days * 24 + hour
+ minutes = hours * 60 + minute
+ seconds = minutes * 60 + second
+ return seconds
+
+
+class _ExtendedMorsel(Morsel):
+ _reserved = {'httponly': 'HttpOnly'}
+ _reserved.update(Morsel._reserved)
+
+ def __init__(self, name=None, value=None):
+ Morsel.__init__(self)
+ if name is not None:
+ self.set(name, value, value)
+
+ def OutputString(self, attrs=None):
+ httponly = self.pop('httponly', False)
+ result = Morsel.OutputString(self, attrs).rstrip('\t ;')
+ if httponly:
+ result += '; HttpOnly'
+ return result
+
+
+class _ExtendedCookie(SimpleCookie):
+ """Form of the base cookie that doesn't raise a `CookieError` for
+ malformed keys. This has the advantage that broken cookies submitted
+ by nonstandard browsers don't cause the cookie to be empty.
+ """
+
+ def _BaseCookie__set(self, key, real_value, coded_value):
+ morsel = self.get(key, _ExtendedMorsel())
+ try:
+ morsel.set(key, real_value, coded_value)
+ except CookieError:
+ pass
+ dict.__setitem__(self, key, morsel)
+
+
+class _DictAccessorProperty(object):
+ """Baseclass for `environ_property` and `header_property`."""
+ read_only = False
+
+ def __init__(self, name, default=None, load_func=None, dump_func=None,
+ read_only=None, doc=None):
+ self.name = name
+ self.default = default
+ self.load_func = load_func
+ self.dump_func = dump_func
+ if read_only is not None:
+ self.read_only = read_only
+ self.__doc__ = doc
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ storage = self.lookup(obj)
+ if self.name not in storage:
+ return self.default
+ rv = storage[self.name]
+ if self.load_func is not None:
+ try:
+ rv = self.load_func(rv)
+ except (ValueError, TypeError):
+ rv = self.default
+ return rv
+
+ def __set__(self, obj, value):
+ if self.read_only:
+ raise AttributeError('read only property')
+ if self.dump_func is not None:
+ value = self.dump_func(value)
+ self.lookup(obj)[self.name] = value
+
+ def __delete__(self, obj):
+ if self.read_only:
+ raise AttributeError('read only property')
+ self.lookup(obj).pop(self.name, None)
+
+ def __repr__(self):
+ return '<%s %s>' % (
+ self.__class__.__name__,
+ self.name
+ )
+
+
+def _easteregg(app):
+ """Like the name says. But who knows how it works?"""
+ gyver = '\n'.join([x + (77 - len(x)) * ' ' for x in '''
+eJyFlzuOJDkMRP06xRjymKgDJCDQStBYT8BCgK4gTwfQ2fcFs2a2FzvZk+hvlcRvRJD148efHt9m
+9Xz94dRY5hGt1nrYcXx7us9qlcP9HHNh28rz8dZj+q4rynVFFPdlY4zH873NKCexrDM6zxxRymzz
+4QIxzK4bth1PV7+uHn6WXZ5C4ka/+prFzx3zWLMHAVZb8RRUxtFXI5DTQ2n3Hi2sNI+HK43AOWSY
+jmEzE4naFp58PdzhPMdslLVWHTGUVpSxImw+pS/D+JhzLfdS1j7PzUMxij+mc2U0I9zcbZ/HcZxc
+q1QjvvcThMYFnp93agEx392ZdLJWXbi/Ca4Oivl4h/Y1ErEqP+lrg7Xa4qnUKu5UE9UUA4xeqLJ5
+jWlPKJvR2yhRI7xFPdzPuc6adXu6ovwXwRPXXnZHxlPtkSkqWHilsOrGrvcVWXgGP3daXomCj317
+8P2UOw/NnA0OOikZyFf3zZ76eN9QXNwYdD8f8/LdBRFg0BO3bB+Pe/+G8er8tDJv83XTkj7WeMBJ
+v/rnAfdO51d6sFglfi8U7zbnr0u9tyJHhFZNXYfH8Iafv2Oa+DT6l8u9UYlajV/hcEgk1x8E8L/r
+XJXl2SK+GJCxtnyhVKv6GFCEB1OO3f9YWAIEbwcRWv/6RPpsEzOkXURMN37J0PoCSYeBnJQd9Giu
+LxYQJNlYPSo/iTQwgaihbART7Fcyem2tTSCcwNCs85MOOpJtXhXDe0E7zgZJkcxWTar/zEjdIVCk
+iXy87FW6j5aGZhttDBoAZ3vnmlkx4q4mMmCdLtnHkBXFMCReqthSGkQ+MDXLLCpXwBs0t+sIhsDI
+tjBB8MwqYQpLygZ56rRHHpw+OAVyGgaGRHWy2QfXez+ZQQTTBkmRXdV/A9LwH6XGZpEAZU8rs4pE
+1R4FQ3Uwt8RKEtRc0/CrANUoes3EzM6WYcFyskGZ6UTHJWenBDS7h163Eo2bpzqxNE9aVgEM2CqI
+GAJe9Yra4P5qKmta27VjzYdR04Vc7KHeY4vs61C0nbywFmcSXYjzBHdiEjraS7PGG2jHHTpJUMxN
+Jlxr3pUuFvlBWLJGE3GcA1/1xxLcHmlO+LAXbhrXah1tD6Ze+uqFGdZa5FM+3eHcKNaEarutAQ0A
+QMAZHV+ve6LxAwWnXbbSXEG2DmCX5ijeLCKj5lhVFBrMm+ryOttCAeFpUdZyQLAQkA06RLs56rzG
+8MID55vqr/g64Qr/wqwlE0TVxgoiZhHrbY2h1iuuyUVg1nlkpDrQ7Vm1xIkI5XRKLedN9EjzVchu
+jQhXcVkjVdgP2O99QShpdvXWoSwkp5uMwyjt3jiWCqWGSiaaPAzohjPanXVLbM3x0dNskJsaCEyz
+DTKIs+7WKJD4ZcJGfMhLFBf6hlbnNkLEePF8Cx2o2kwmYF4+MzAxa6i+6xIQkswOqGO+3x9NaZX8
+MrZRaFZpLeVTYI9F/djY6DDVVs340nZGmwrDqTCiiqD5luj3OzwpmQCiQhdRYowUYEA3i1WWGwL4
+GCtSoO4XbIPFeKGU13XPkDf5IdimLpAvi2kVDVQbzOOa4KAXMFlpi/hV8F6IDe0Y2reg3PuNKT3i
+RYhZqtkQZqSB2Qm0SGtjAw7RDwaM1roESC8HWiPxkoOy0lLTRFG39kvbLZbU9gFKFRvixDZBJmpi
+Xyq3RE5lW00EJjaqwp/v3EByMSpVZYsEIJ4APaHmVtpGSieV5CALOtNUAzTBiw81GLgC0quyzf6c
+NlWknzJeCsJ5fup2R4d8CYGN77mu5vnO1UqbfElZ9E6cR6zbHjgsr9ly18fXjZoPeDjPuzlWbFwS
+pdvPkhntFvkc13qb9094LL5NrA3NIq3r9eNnop9DizWOqCEbyRBFJTHn6Tt3CG1o8a4HevYh0XiJ
+sR0AVVHuGuMOIfbuQ/OKBkGRC6NJ4u7sbPX8bG/n5sNIOQ6/Y/BX3IwRlTSabtZpYLB85lYtkkgm
+p1qXK3Du2mnr5INXmT/78KI12n11EFBkJHHp0wJyLe9MvPNUGYsf+170maayRoy2lURGHAIapSpQ
+krEDuNoJCHNlZYhKpvw4mspVWxqo415n8cD62N9+EfHrAvqQnINStetek7RY2Urv8nxsnGaZfRr/
+nhXbJ6m/yl1LzYqscDZA9QHLNbdaSTTr+kFg3bC0iYbX/eQy0Bv3h4B50/SGYzKAXkCeOLI3bcAt
+mj2Z/FM1vQWgDynsRwNvrWnJHlespkrp8+vO1jNaibm+PhqXPPv30YwDZ6jApe3wUjFQobghvW9p
+7f2zLkGNv8b191cD/3vs9Q833z8t'''.decode('base64').decode('zlib').splitlines()])
+ def easteregged(environ, start_response):
+ def injecting_start_response(status, headers, exc_info=None):
+ headers.append(('X-Powered-By', 'Werkzeug'))
+ return start_response(status, headers, exc_info)
+ if environ.get('QUERY_STRING') != 'macgybarchakku':
+ return app(environ, injecting_start_response)
+ injecting_start_response('200 OK', [('Content-Type', 'text/html')])
+ return ['''
+<!DOCTYPE html>
+<html>
+<head>
+<title>About Werkzeug</title>
+<style type="text/css">
+ body { font: 15px Georgia, serif; text-align: center; }
+ a { color: #333; text-decoration: none; }
+ h1 { font-size: 30px; margin: 20px 0 10px 0; }
+ p { margin: 0 0 30px 0; }
+ pre { font: 11px 'Consolas', 'Monaco', monospace; line-height: 0.95; }
+</style>
+</head>
+<body>
+<h1><a href="http://werkzeug.pocoo.org/">Werkzeug</a></h1>
+<p>the Swiss Army knife of Python web development.</p>
+<pre>%s\n\n\n</pre>
+</body>
+</html>''' % gyver]
+ return easteregged
diff --git a/websdk/werkzeug/contrib/__init__.py b/websdk/werkzeug/contrib/__init__.py
new file mode 100644
index 0000000..ffc48c9
--- /dev/null
+++ b/websdk/werkzeug/contrib/__init__.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib
+ ~~~~~~~~~~~~~~~~
+
+ Contains user-submitted code that other users may find useful, but which
+ is not part of the Werkzeug core. Anyone can write code for inclusion in
+ the `contrib` package. All modules in this package are distributed as an
+ add-on library and thus are not part of Werkzeug itself.
+
+ This file itself is mostly for informational purposes and to tell the
+ Python interpreter that `contrib` is a package.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
diff --git a/websdk/werkzeug/contrib/atom.py b/websdk/werkzeug/contrib/atom.py
new file mode 100644
index 0000000..7aaa2fb
--- /dev/null
+++ b/websdk/werkzeug/contrib/atom.py
@@ -0,0 +1,343 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib.atom
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ This module provides a class called :class:`AtomFeed` which can be
+ used to generate feeds in the Atom syndication format (see :rfc:`4287`).
+
+ Example::
+
+ def atom_feed(request):
+ feed = AtomFeed("My Blog", feed_url=request.url,
+ url=request.host_url,
+ subtitle="My example blog for a feed test.")
+ for post in Post.query.limit(10).all():
+ feed.add(post.title, post.body, content_type='html',
+ author=post.author, url=post.url, id=post.uid,
+ updated=post.last_update, published=post.pub_date)
+ return feed.get_response()
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+from datetime import datetime
+from werkzeug.utils import escape
+from werkzeug.wrappers import BaseResponse
+
+
+XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'
+
+
+def _make_text_block(name, content, content_type=None):
+ """Helper function for the builder that creates an XML text block."""
+ if content_type == 'xhtml':
+ return u'<%s type="xhtml"><div xmlns="%s">%s</div></%s>\n' % \
+ (name, XHTML_NAMESPACE, content, name)
+ if not content_type:
+ return u'<%s>%s</%s>\n' % (name, escape(content), name)
+ return u'<%s type="%s">%s</%s>\n' % (name, content_type,
+ escape(content), name)
+
+
+def format_iso8601(obj):
+ """Format a datetime object for iso8601"""
+ return obj.strftime('%Y-%m-%dT%H:%M:%SZ')
+
+
+class AtomFeed(object):
+ """A helper class that creates Atom feeds.
+
+ :param title: the title of the feed. Required.
+ :param title_type: the type attribute for the title element. One of
+ ``'html'``, ``'text'`` or ``'xhtml'``.
+ :param url: the url for the feed (not the url *of* the feed)
+ :param id: a globally unique id for the feed. Must be an URI. If
+ not present the `feed_url` is used, but one of both is
+ required.
+ :param updated: the time the feed was modified the last time. Must
+ be a :class:`datetime.datetime` object. If not
+ present the latest entry's `updated` is used.
+ :param feed_url: the URL to the feed. Should be the URL that was
+ requested.
+ :param author: the author of the feed. Must be either a string (the
+ name) or a dict with name (required) and uri or
+ email (both optional). Can be a list of (may be
+ mixed, too) strings and dicts, too, if there are
+ multiple authors. Required if not every entry has an
+ author element.
+ :param icon: an icon for the feed.
+ :param logo: a logo for the feed.
+ :param rights: copyright information for the feed.
+ :param rights_type: the type attribute for the rights element. One of
+ ``'html'``, ``'text'`` or ``'xhtml'``. Default is
+ ``'text'``.
+ :param subtitle: a short description of the feed.
+ :param subtitle_type: the type attribute for the subtitle element.
+ One of ``'text'``, ``'html'``, ``'text'``
+ or ``'xhtml'``. Default is ``'text'``.
+ :param links: additional links. Must be a list of dictionaries with
+ href (required) and rel, type, hreflang, title, length
+ (all optional)
+ :param generator: the software that generated this feed. This must be
+ a tuple in the form ``(name, url, version)``. If
+ you don't want to specify one of them, set the item
+ to `None`.
+ :param entries: a list with the entries for the feed. Entries can also
+ be added later with :meth:`add`.
+
+ For more information on the elements see
+ http://www.atomenabled.org/developers/syndication/
+
+ Everywhere where a list is demanded, any iterable can be used.
+ """
+
+ default_generator = ('Werkzeug', None, None)
+
+ def __init__(self, title=None, entries=None, **kwargs):
+ self.title = title
+ self.title_type = kwargs.get('title_type', 'text')
+ self.url = kwargs.get('url')
+ self.feed_url = kwargs.get('feed_url', self.url)
+ self.id = kwargs.get('id', self.feed_url)
+ self.updated = kwargs.get('updated')
+ self.author = kwargs.get('author', ())
+ self.icon = kwargs.get('icon')
+ self.logo = kwargs.get('logo')
+ self.rights = kwargs.get('rights')
+ self.rights_type = kwargs.get('rights_type')
+ self.subtitle = kwargs.get('subtitle')
+ self.subtitle_type = kwargs.get('subtitle_type', 'text')
+ self.generator = kwargs.get('generator')
+ if self.generator is None:
+ self.generator = self.default_generator
+ self.links = kwargs.get('links', [])
+ self.entries = entries and list(entries) or []
+
+ if not hasattr(self.author, '__iter__') \
+ or isinstance(self.author, (basestring, dict)):
+ self.author = [self.author]
+ for i, author in enumerate(self.author):
+ if not isinstance(author, dict):
+ self.author[i] = {'name': author}
+
+ if not self.title:
+ raise ValueError('title is required')
+ if not self.id:
+ raise ValueError('id is required')
+ for author in self.author:
+ if 'name' not in author:
+ raise TypeError('author must contain at least a name')
+
+ def add(self, *args, **kwargs):
+ """Add a new entry to the feed. This function can either be called
+ with a :class:`FeedEntry` or some keyword and positional arguments
+ that are forwarded to the :class:`FeedEntry` constructor.
+ """
+ if len(args) == 1 and not kwargs and isinstance(args[0], FeedEntry):
+ self.entries.append(args[0])
+ else:
+ kwargs['feed_url'] = self.feed_url
+ self.entries.append(FeedEntry(*args, **kwargs))
+
+ def __repr__(self):
+ return '<%s %r (%d entries)>' % (
+ self.__class__.__name__,
+ self.title,
+ len(self.entries)
+ )
+
+ def generate(self):
+ """Return a generator that yields pieces of XML."""
+ # atom demands either an author element in every entry or a global one
+ if not self.author:
+ if False in map(lambda e: bool(e.author), self.entries):
+ self.author = ({'name': 'Unknown author'},)
+
+ if not self.updated:
+ dates = sorted([entry.updated for entry in self.entries])
+ self.updated = dates and dates[-1] or datetime.utcnow()
+
+ yield u'<?xml version="1.0" encoding="utf-8"?>\n'
+ yield u'<feed xmlns="http://www.w3.org/2005/Atom">\n'
+ yield ' ' + _make_text_block('title', self.title, self.title_type)
+ yield u' <id>%s</id>\n' % escape(self.id)
+ yield u' <updated>%s</updated>\n' % format_iso8601(self.updated)
+ if self.url:
+ yield u' <link href="%s" />\n' % escape(self.url, True)
+ if self.feed_url:
+ yield u' <link href="%s" rel="self" />\n' % \
+ escape(self.feed_url, True)
+ for link in self.links:
+ yield u' <link %s/>\n' % ''.join('%s="%s" ' % \
+ (k, escape(link[k], True)) for k in link)
+ for author in self.author:
+ yield u' <author>\n'
+ yield u' <name>%s</name>\n' % escape(author['name'])
+ if 'uri' in author:
+ yield u' <uri>%s</uri>\n' % escape(author['uri'])
+ if 'email' in author:
+ yield ' <email>%s</email>\n' % escape(author['email'])
+ yield ' </author>\n'
+ if self.subtitle:
+ yield ' ' + _make_text_block('subtitle', self.subtitle,
+ self.subtitle_type)
+ if self.icon:
+ yield u' <icon>%s</icon>\n' % escape(self.icon)
+ if self.logo:
+ yield u' <logo>%s</logo>\n' % escape(self.logo)
+ if self.rights:
+ yield ' ' + _make_text_block('rights', self.rights,
+ self.rights_type)
+ generator_name, generator_url, generator_version = self.generator
+ if generator_name or generator_url or generator_version:
+ tmp = [u' <generator']
+ if generator_url:
+ tmp.append(u' uri="%s"' % escape(generator_url, True))
+ if generator_version:
+ tmp.append(u' version="%s"' % escape(generator_version, True))
+ tmp.append(u'>%s</generator>\n' % escape(generator_name))
+ yield u''.join(tmp)
+ for entry in self.entries:
+ for line in entry.generate():
+ yield u' ' + line
+ yield u'</feed>\n'
+
+ def to_string(self):
+ """Convert the feed into a string."""
+ return u''.join(self.generate())
+
+ def get_response(self):
+ """Return a response object for the feed."""
+ return BaseResponse(self.to_string(), mimetype='application/atom+xml')
+
+ def __call__(self, environ, start_response):
+ """Use the class as WSGI response object."""
+ return self.get_response()(environ, start_response)
+
+ def __unicode__(self):
+ return self.to_string()
+
+ def __str__(self):
+ return self.to_string().encode('utf-8')
+
+
+class FeedEntry(object):
+ """Represents a single entry in a feed.
+
+ :param title: the title of the entry. Required.
+ :param title_type: the type attribute for the title element. One of
+ ``'html'``, ``'text'`` or ``'xhtml'``.
+ :param content: the content of the entry.
+ :param content_type: the type attribute for the content element. One
+ of ``'html'``, ``'text'`` or ``'xhtml'``.
+ :param summary: a summary of the entry's content.
+ :param summary_type: the type attribute for the summary element. One
+ of ``'html'``, ``'text'`` or ``'xhtml'``.
+ :param url: the url for the entry.
+ :param id: a globally unique id for the entry. Must be an URI. If
+ not present the URL is used, but one of both is required.
+ :param updated: the time the entry was modified the last time. Must
+ be a :class:`datetime.datetime` object. Required.
+ :param author: the author of the feed. Must be either a string (the
+ name) or a dict with name (required) and uri or
+ email (both optional). Can be a list of (may be
+ mixed, too) strings and dicts, too, if there are
+ multiple authors. Required if not every entry has an
+ author element.
+ :param published: the time the entry was initially published. Must
+ be a :class:`datetime.datetime` object.
+ :param rights: copyright information for the entry.
+ :param rights_type: the type attribute for the rights element. One of
+ ``'html'``, ``'text'`` or ``'xhtml'``. Default is
+ ``'text'``.
+ :param links: additional links. Must be a list of dictionaries with
+ href (required) and rel, type, hreflang, title, length
+ (all optional)
+ :param xml_base: The xml base (url) for this feed item. If not provided
+ it will default to the item url.
+
+ For more information on the elements see
+ http://www.atomenabled.org/developers/syndication/
+
+ Everywhere where a list is demanded, any iterable can be used.
+ """
+
+ def __init__(self, title=None, content=None, feed_url=None, **kwargs):
+ self.title = title
+ self.title_type = kwargs.get('title_type', 'text')
+ self.content = content
+ self.content_type = kwargs.get('content_type', 'html')
+ self.url = kwargs.get('url')
+ self.id = kwargs.get('id', self.url)
+ self.updated = kwargs.get('updated')
+ self.summary = kwargs.get('summary')
+ self.summary_type = kwargs.get('summary_type', 'html')
+ self.author = kwargs.get('author')
+ self.published = kwargs.get('published')
+ self.rights = kwargs.get('rights')
+ self.links = kwargs.get('links', [])
+ self.xml_base = kwargs.get('xml_base', feed_url)
+
+ if not hasattr(self.author, '__iter__') \
+ or isinstance(self.author, (basestring, dict)):
+ self.author = [self.author]
+ for i, author in enumerate(self.author):
+ if not isinstance(author, dict):
+ self.author[i] = {'name': author}
+
+ if not self.title:
+ raise ValueError('title is required')
+ if not self.id:
+ raise ValueError('id is required')
+ if not self.updated:
+ raise ValueError('updated is required')
+
+ def __repr__(self):
+ return '<%s %r>' % (
+ self.__class__.__name__,
+ self.title
+ )
+
+ def generate(self):
+ """Yields pieces of ATOM XML."""
+ base = ''
+ if self.xml_base:
+ base = ' xml:base="%s"' % escape(self.xml_base, True)
+ yield u'<entry%s>\n' % base
+ yield u' ' + _make_text_block('title', self.title, self.title_type)
+ yield u' <id>%s</id>\n' % escape(self.id)
+ yield u' <updated>%s</updated>\n' % format_iso8601(self.updated)
+ if self.published:
+ yield u' <published>%s</published>\n' % \
+ format_iso8601(self.published)
+ if self.url:
+ yield u' <link href="%s" />\n' % escape(self.url)
+ for author in self.author:
+ yield u' <author>\n'
+ yield u' <name>%s</name>\n' % escape(author['name'])
+ if 'uri' in author:
+ yield u' <uri>%s</uri>\n' % escape(author['uri'])
+ if 'email' in author:
+ yield u' <email>%s</email>\n' % escape(author['email'])
+ yield u' </author>\n'
+ for link in self.links:
+ yield u' <link %s/>\n' % ''.join('%s="%s" ' % \
+ (k, escape(link[k], True)) for k in link)
+ if self.summary:
+ yield u' ' + _make_text_block('summary', self.summary,
+ self.summary_type)
+ if self.content:
+ yield u' ' + _make_text_block('content', self.content,
+ self.content_type)
+ yield u'</entry>\n'
+
+ def to_string(self):
+ """Convert the feed item into a unicode object."""
+ return u''.join(self.generate())
+
+ def __unicode__(self):
+ return self.to_string()
+
+ def __str__(self):
+ return self.to_string().encode('utf-8')
diff --git a/websdk/werkzeug/contrib/cache.py b/websdk/werkzeug/contrib/cache.py
new file mode 100644
index 0000000..0526c48
--- /dev/null
+++ b/websdk/werkzeug/contrib/cache.py
@@ -0,0 +1,635 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib.cache
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ The main problem with dynamic Web sites is, well, they're dynamic. Each
+ time a user requests a page, the webserver executes a lot of code, queries
+ the database, renders templates until the visitor gets the page he sees.
+
+ This is a lot more expensive than just loading a file from the file system
+ and sending it to the visitor.
+
+ For most Web applications, this overhead isn't a big deal but once it
+ becomes, you will be glad to have a cache system in place.
+
+ How Caching Works
+ =================
+
+ Caching is pretty simple. Basically you have a cache object lurking around
+ somewhere that is connected to a remote cache or the file system or
+ something else. When the request comes in you check if the current page
+ is already in the cache and if so, you're returning it from the cache.
+ Otherwise you generate the page and put it into the cache. (Or a fragment
+ of the page, you don't have to cache the full thing)
+
+ Here is a simple example of how to cache a sidebar for a template::
+
+ def get_sidebar(user):
+ identifier = 'sidebar_for/user%d' % user.id
+ value = cache.get(identifier)
+ if value is not None:
+ return value
+ value = generate_sidebar_for(user=user)
+ cache.set(identifier, value, timeout=60 * 5)
+ return value
+
+ Creating a Cache Object
+ =======================
+
+ To create a cache object you just import the cache system of your choice
+ from the cache module and instantiate it. Then you can start working
+ with that object:
+
+ >>> from werkzeug.contrib.cache import SimpleCache
+ >>> c = SimpleCache()
+ >>> c.set("foo", "value")
+ >>> c.get("foo")
+ 'value'
+ >>> c.get("missing") is None
+ True
+
+ Please keep in mind that you have to create the cache and put it somewhere
+ you have access to it (either as a module global you can import or you just
+ put it into your WSGI application).
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import re
+import tempfile
+try:
+ from hashlib import md5
+except ImportError:
+ from md5 import new as md5
+from itertools import izip
+from time import time
+from cPickle import loads, dumps, load, dump, HIGHEST_PROTOCOL
+from werkzeug.posixemulation import rename
+
+def _items(mappingorseq):
+ """Wrapper for efficient iteration over mappings represented by dicts
+ or sequences::
+
+ >>> for k, v in _items((i, i*i) for i in xrange(5)):
+ ... assert k*k == v
+
+ >>> for k, v in _items(dict((i, i*i) for i in xrange(5))):
+ ... assert k*k == v
+
+ """
+ return mappingorseq.iteritems() if hasattr(mappingorseq, 'iteritems') \
+ else mappingorseq
+
+class BaseCache(object):
+ """Baseclass for the cache systems. All the cache systems implement this
+ API or a superset of it.
+
+ :param default_timeout: the default timeout that is used if no timeout is
+ specified on :meth:`set`.
+ """
+
+ def __init__(self, default_timeout=300):
+ self.default_timeout = default_timeout
+
+ def get(self, key):
+ """Looks up key in the cache and returns the value for it.
+ If the key does not exist `None` is returned instead.
+
+ :param key: the key to be looked up.
+ """
+ return None
+
+ def delete(self, key):
+ """Deletes `key` from the cache. If it does not exist in the cache
+ nothing happens.
+
+ :param key: the key to delete.
+ """
+ pass
+
+ def get_many(self, *keys):
+ """Returns a list of values for the given keys.
+ For each key a item in the list is created. Example::
+
+ foo, bar = cache.get_many("foo", "bar")
+
+ If a key can't be looked up `None` is returned for that key
+ instead.
+
+ :param keys: The function accepts multiple keys as positional
+ arguments.
+ """
+ return map(self.get, keys)
+
+ def get_dict(self, *keys):
+ """Works like :meth:`get_many` but returns a dict::
+
+ d = cache.get_dict("foo", "bar")
+ foo = d["foo"]
+ bar = d["bar"]
+
+ :param keys: The function accepts multiple keys as positional
+ arguments.
+ """
+ return dict(izip(keys, self.get_many(*keys)))
+
+ def set(self, key, value, timeout=None):
+ """Adds a new key/value to the cache (overwrites value, if key already
+ exists in the cache).
+
+ :param key: the key to set
+ :param value: the value for the key
+ :param timeout: the cache timeout for the key (if not specified,
+ it uses the default timeout).
+ """
+ pass
+
+ def add(self, key, value, timeout=None):
+ """Works like :meth:`set` but does not overwrite the values of already
+ existing keys.
+
+ :param key: the key to set
+ :param value: the value for the key
+ :param timeout: the cache timeout for the key or the default
+ timeout if not specified.
+ """
+ pass
+
+ def set_many(self, mapping, timeout=None):
+ """Sets multiple keys and values from a mapping.
+
+ :param mapping: a mapping with the keys/values to set.
+ :param timeout: the cache timeout for the key (if not specified,
+ it uses the default timeout).
+ """
+ for key, value in _items(mapping):
+ self.set(key, value, timeout)
+
+ def delete_many(self, *keys):
+ """Deletes multiple keys at once.
+
+ :param keys: The function accepts multiple keys as positional
+ arguments.
+ """
+ for key in keys:
+ self.delete(key)
+
+ def clear(self):
+ """Clears the cache. Keep in mind that not all caches support
+ completely clearing the cache.
+ """
+ pass
+
+ def inc(self, key, delta=1):
+ """Increments the value of a key by `delta`. If the key does
+ not yet exist it is initialized with `delta`.
+
+ For supporting caches this is an atomic operation.
+
+ :param key: the key to increment.
+ :param delta: the delta to add.
+ """
+ self.set(key, (self.get(key) or 0) + delta)
+
+ def dec(self, key, delta=1):
+ """Decrements the value of a key by `delta`. If the key does
+ not yet exist it is initialized with `-delta`.
+
+ For supporting caches this is an atomic operation.
+
+ :param key: the key to increment.
+ :param delta: the delta to subtract.
+ """
+ self.set(key, (self.get(key) or 0) - delta)
+
+
+class NullCache(BaseCache):
+ """A cache that doesn't cache. This can be useful for unit testing.
+
+ :param default_timeout: a dummy parameter that is ignored but exists
+ for API compatibility with other caches.
+ """
+
+
+class SimpleCache(BaseCache):
+ """Simple memory cache for single process environments. This class exists
+ mainly for the development server and is not 100% thread safe. It tries
+ to use as many atomic operations as possible and no locks for simplicity
+ but it could happen under heavy load that keys are added multiple times.
+
+ :param threshold: the maximum number of items the cache stores before
+ it starts deleting some.
+ :param default_timeout: the default timeout that is used if no timeout is
+ specified on :meth:`~BaseCache.set`.
+ """
+
+ def __init__(self, threshold=500, default_timeout=300):
+ BaseCache.__init__(self, default_timeout)
+ self._cache = {}
+ self.clear = self._cache.clear
+ self._threshold = threshold
+
+ def _prune(self):
+ if len(self._cache) > self._threshold:
+ now = time()
+ for idx, (key, (expires, _)) in enumerate(self._cache.items()):
+ if expires <= now or idx % 3 == 0:
+ self._cache.pop(key, None)
+
+ def get(self, key):
+ now = time()
+ expires, value = self._cache.get(key, (0, None))
+ if expires > time():
+ return loads(value)
+
+ def set(self, key, value, timeout=None):
+ if timeout is None:
+ timeout = self.default_timeout
+ self._prune()
+ self._cache[key] = (time() + timeout, dumps(value, HIGHEST_PROTOCOL))
+
+ def add(self, key, value, timeout=None):
+ if timeout is None:
+ timeout = self.default_timeout
+ if len(self._cache) > self._threshold:
+ self._prune()
+ item = (time() + timeout, dumps(value, HIGHEST_PROTOCOL))
+ self._cache.setdefault(key, item)
+
+ def delete(self, key):
+ self._cache.pop(key, None)
+
+
+_test_memcached_key = re.compile(r'[^\x00-\x21\xff]{1,250}$').match
+
+class MemcachedCache(BaseCache):
+ """A cache that uses memcached as backend.
+
+ The first argument can either be an object that resembles the API of a
+ :class:`memcache.Client` or a tuple/list of server addresses. In the
+ event that a tuple/list is passed, Werkzeug tries to import the best
+ available memcache library.
+
+ Implementation notes: This cache backend works around some limitations in
+ memcached to simplify the interface. For example unicode keys are encoded
+ to utf-8 on the fly. Methods such as :meth:`~BaseCache.get_dict` return
+ the keys in the same format as passed. Furthermore all get methods
+ silently ignore key errors to not cause problems when untrusted user data
+ is passed to the get methods which is often the case in web applications.
+
+ :param servers: a list or tuple of server addresses or alternatively
+ a :class:`memcache.Client` or a compatible client.
+ :param default_timeout: the default timeout that is used if no timeout is
+ specified on :meth:`~BaseCache.set`.
+ :param key_prefix: a prefix that is added before all keys. This makes it
+ possible to use the same memcached server for different
+ applications. Keep in mind that
+ :meth:`~BaseCache.clear` will also clear keys with a
+ different prefix.
+ """
+
+ def __init__(self, servers, default_timeout=300, key_prefix=None):
+ BaseCache.__init__(self, default_timeout)
+ if isinstance(servers, (list, tuple)):
+ self._client = self.import_preferred_memcache_lib(servers)
+ else:
+ # NOTE: servers is actually an already initialized memcache
+ # client.
+ self._client = servers
+
+ self.key_prefix = key_prefix
+
+
+ def get(self, key):
+ if isinstance(key, unicode):
+ key = key.encode('utf-8')
+ if self.key_prefix:
+ key = self.key_prefix + key
+ # memcached doesn't support keys longer than that. Because often
+ # checks for so long keys can occour because it's tested from user
+ # submitted data etc we fail silently for getting.
+ if _test_memcached_key(key):
+ return self._client.get(key)
+
+ def get_dict(self, *keys):
+ key_mapping = {}
+ have_encoded_keys = False
+ for key in keys:
+ if isinstance(key, unicode):
+ encoded_key = key.encode('utf-8')
+ have_encoded_keys = True
+ else:
+ encoded_key = key
+ if self.key_prefix:
+ encoded_key = self.key_prefix + encoded_key
+ if _test_memcached_key(key):
+ key_mapping[encoded_key] = key
+ d = rv = self._client.get_multi(key_mapping.keys())
+ if have_encoded_keys or self.key_prefix:
+ rv = {}
+ for key, value in d.iteritems():
+ rv[key_mapping[key]] = value
+ if len(rv) < len(keys):
+ for key in keys:
+ if key not in rv:
+ rv[key] = None
+ return rv
+
+ def add(self, key, value, timeout=None):
+ if timeout is None:
+ timeout = self.default_timeout
+ if isinstance(key, unicode):
+ key = key.encode('utf-8')
+ if self.key_prefix:
+ key = self.key_prefix + key
+ self._client.add(key, value, timeout)
+
+ def set(self, key, value, timeout=None):
+ if timeout is None:
+ timeout = self.default_timeout
+ if isinstance(key, unicode):
+ key = key.encode('utf-8')
+ if self.key_prefix:
+ key = self.key_prefix + key
+ self._client.set(key, value, timeout)
+
+ def get_many(self, *keys):
+ d = self.get_dict(*keys)
+ return [d[key] for key in keys]
+
+ def set_many(self, mapping, timeout=None):
+ if timeout is None:
+ timeout = self.default_timeout
+ new_mapping = {}
+ for key, value in _items(mapping):
+ if isinstance(key, unicode):
+ key = key.encode('utf-8')
+ if self.key_prefix:
+ key = self.key_prefix + key
+ new_mapping[key] = value
+ self._client.set_multi(new_mapping, timeout)
+
+ def delete(self, key):
+ if isinstance(key, unicode):
+ key = key.encode('utf-8')
+ if self.key_prefix:
+ key = self.key_prefix + key
+ if _test_memcached_key(key):
+ self._client.delete(key)
+
+ def delete_many(self, *keys):
+ new_keys = []
+ for key in keys:
+ if isinstance(key, unicode):
+ key = key.encode('utf-8')
+ if self.key_prefix:
+ key = self.key_prefix + key
+ if _test_memcached_key(key):
+ new_keys.append(key)
+ self._client.delete_multi(new_keys)
+
+ def clear(self):
+ self._client.flush_all()
+
+ def inc(self, key, delta=1):
+ if isinstance(key, unicode):
+ key = key.encode('utf-8')
+ if self.key_prefix:
+ key = self.key_prefix + key
+ self._client.incr(key, delta)
+
+ def dec(self, key, delta=1):
+ if isinstance(key, unicode):
+ key = key.encode('utf-8')
+ if self.key_prefix:
+ key = self.key_prefix + key
+ self._client.decr(key, delta)
+
+
+ def import_preferred_memcache_lib(self, servers):
+ """ Returns an initialized memcache client """
+ try:
+ import pylibmc
+ return pylibmc.Client(servers)
+ except ImportError:
+ pass
+
+ try:
+ from google.appengine.api import memcache
+ return memcache.Client()
+ except ImportError:
+ pass
+
+ try:
+ import memcache
+ return memcache.Client(servers)
+ except ImportError:
+ pass
+
+ # If you're seeing this, either you need to install a memcache client
+ # or you need to monkey patch this method to support your
+ # environment.
+ raise RuntimeError('no memcache module found')
+
+
+# backwards compatibility
+GAEMemcachedCache = MemcachedCache
+
+
+class RedisCache(BaseCache):
+ """Uses the Redis key-value store as a cache backend.
+
+ The first argument can be either a string denoting address of the Redis
+ server or an object resembling an instance of a redis.Redis class.
+
+ Note: Python Redis API already takes care of encoding unicode strings on
+ the fly.
+
+ .. versionadded:: 0.7
+
+ .. versionadded:: 0.8
+ `key_prefix` was added.
+
+ :param host: address of the Redis server or an object which API is
+ compatible with the official Python Redis client (redis-py).
+ :param port: port number on which Redis server listens for connections
+ :param default_timeout: the default timeout that is used if no timeout is
+ specified on :meth:`~BaseCache.set`.
+ :param key_prefix: A prefix that should be added to all keys.
+ """
+ def __init__(self, host='localhost', port=6379, default_timeout=300,
+ key_prefix=None):
+ BaseCache.__init__(self, default_timeout)
+ if isinstance(host, basestring):
+ try:
+ import redis
+ except ImportError:
+ raise RuntimeError('no redis module found')
+ self._client = redis.Redis(host=host, port=port)
+ else:
+ self._client = host
+ self.key_prefix = key_prefix or ''
+
+ def get(self, key):
+ return self._client.get(self.key_prefix + key)
+
+ def get_many(self, *keys):
+ if self.key_prefix:
+ keys = [self.key_prefix + key for key in keys]
+ return self._client.mget(keys)
+
+ def set(self, key, value, timeout=None):
+ if timeout is None:
+ timeout = self.default_timeout
+ self._client.setex(self.key_prefix + key, value, timeout)
+
+ def add(self, key, value, timeout=None):
+ if timeout is None:
+ timeout = self.default_timeout
+ added = self._client.setnx(self.key_prefix + key, value)
+ if added:
+ self._client.expire(self.key_prefix + key, timeout)
+
+ def set_many(self, mapping, timeout=None):
+ if timeout is None:
+ timeout = self.default_timeout
+ pipe = self._client.pipeline()
+ for key, value in _items(mapping):
+ pipe.setex(self.key_prefix + key, value, timeout)
+ pipe.execute()
+
+ def delete(self, key):
+ self._client.delete(self.key_prefix + key)
+
+ def delete_many(self, *keys):
+ if not keys:
+ return
+ if self.key_prefix:
+ keys = [self.key_prefix + key for key in keys]
+ self._client.delete(*keys)
+
+ def clear(self):
+ if self.key_prefix:
+ keys = self._client.keys(self.key_prefix + '*')
+ if keys:
+ self._client.delete(*keys)
+ else:
+ self._client.flushdb()
+
+ def inc(self, key, delta=1):
+ return self._client.incr(self.key_prefix + key, delta)
+
+ def dec(self, key, delta=1):
+ return self._client.decr(self.key_prefix + key, delta)
+
+
+class FileSystemCache(BaseCache):
+ """A cache that stores the items on the file system. This cache depends
+ on being the only user of the `cache_dir`. Make absolutely sure that
+ nobody but this cache stores files there or otherwise the cache will
+ randomly delete files therein.
+
+ :param cache_dir: the directory where cache files are stored.
+ :param threshold: the maximum number of items the cache stores before
+ it starts deleting some.
+ :param default_timeout: the default timeout that is used if no timeout is
+ specified on :meth:`~BaseCache.set`.
+ :param mode: the file mode wanted for the cache files, default 0600
+ """
+
+ #: used for temporary files by the FileSystemCache
+ _fs_transaction_suffix = '.__wz_cache'
+
+ def __init__(self, cache_dir, threshold=500, default_timeout=300, mode=0600):
+ BaseCache.__init__(self, default_timeout)
+ self._path = cache_dir
+ self._threshold = threshold
+ self._mode = mode
+ if not os.path.exists(self._path):
+ os.makedirs(self._path)
+
+ def _list_dir(self):
+ """return a list of (fully qualified) cache filenames
+ """
+ return [os.path.join(self._path, fn) for fn in os.listdir(self._path)
+ if not fn.endswith(self._fs_transaction_suffix)]
+
+ def _prune(self):
+ entries = self._list_dir()
+ if len(entries) > self._threshold:
+ now = time()
+ for idx, fname in enumerate(entries):
+ remove = False
+ f = None
+ try:
+ try:
+ f = open(fname, 'rb')
+ expires = load(f)
+ remove = expires <= now or idx % 3 == 0
+ finally:
+ if f is not None:
+ f.close()
+ except Exception:
+ pass
+ if remove:
+ try:
+ os.remove(fname)
+ except (IOError, OSError):
+ pass
+
+ def clear(self):
+ for fname in self._list_dir():
+ try:
+ os.remove(fname)
+ except (IOError, OSError):
+ pass
+
+ def _get_filename(self, key):
+ hash = md5(key).hexdigest()
+ return os.path.join(self._path, hash)
+
+ def get(self, key):
+ filename = self._get_filename(key)
+ try:
+ f = open(filename, 'rb')
+ try:
+ if load(f) >= time():
+ return load(f)
+ finally:
+ f.close()
+ os.remove(filename)
+ except Exception:
+ return None
+
+ def add(self, key, value, timeout=None):
+ filename = self._get_filename(key)
+ if not os.path.exists(filename):
+ self.set(key, value, timeout)
+
+ def set(self, key, value, timeout=None):
+ if timeout is None:
+ timeout = self.default_timeout
+ filename = self._get_filename(key)
+ self._prune()
+ try:
+ fd, tmp = tempfile.mkstemp(suffix=self._fs_transaction_suffix,
+ dir=self._path)
+ f = os.fdopen(fd, 'wb')
+ try:
+ dump(int(time() + timeout), f, 1)
+ dump(value, f, HIGHEST_PROTOCOL)
+ finally:
+ f.close()
+ rename(tmp, filename)
+ os.chmod(filename, self._mode)
+ except (IOError, OSError):
+ pass
+
+ def delete(self, key):
+ try:
+ os.remove(self._get_filename(key))
+ except (IOError, OSError):
+ pass
+
diff --git a/websdk/werkzeug/contrib/fixers.py b/websdk/werkzeug/contrib/fixers.py
new file mode 100644
index 0000000..6286e6c
--- /dev/null
+++ b/websdk/werkzeug/contrib/fixers.py
@@ -0,0 +1,224 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib.fixers
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ .. versionadded:: 0.5
+
+ This module includes various helpers that fix bugs in web servers. They may
+ be necessary for some versions of a buggy web server but not others. We try
+ to stay updated with the status of the bugs as good as possible but you have
+ to make sure whether they fix the problem you encounter.
+
+ If you notice bugs in webservers not fixed in this module consider
+ contributing a patch.
+
+ :copyright: Copyright 2009 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+from urllib import unquote
+from werkzeug.http import parse_options_header, parse_cache_control_header, \
+ parse_set_header
+from werkzeug.useragents import UserAgent
+from werkzeug.datastructures import Headers, ResponseCacheControl
+
+
+class LighttpdCGIRootFix(object):
+ """Wrap the application in this middleware if you are using lighttpd
+ with FastCGI or CGI and the application is mounted on the URL root.
+
+ :param app: the WSGI application
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ # only set PATH_INFO for older versions of Lighty or if no
+ # server software is provided. That's because the test was
+ # added in newer Werkzeug versions and we don't want to break
+ # people's code if they are using this fixer in a test that
+ # does not set the SERVER_SOFTWARE key.
+ if 'SERVER_SOFTWARE' not in environ or \
+ environ['SERVER_SOFTWARE'] < 'lighttpd/1.4.28':
+ environ['PATH_INFO'] = environ.get('SCRIPT_NAME', '') + \
+ environ.get('PATH_INFO', '')
+ environ['SCRIPT_NAME'] = ''
+ return self.app(environ, start_response)
+
+
+class PathInfoFromRequestUriFix(object):
+ """On windows environment variables are limited to the system charset
+ which makes it impossible to store the `PATH_INFO` variable in the
+ environment without loss of information on some systems.
+
+ This is for example a problem for CGI scripts on a Windows Apache.
+
+ This fixer works by recreating the `PATH_INFO` from `REQUEST_URI`,
+ `REQUEST_URL`, or `UNENCODED_URL` (whatever is available). Thus the
+ fix can only be applied if the webserver supports either of these
+ variables.
+
+ :param app: the WSGI application
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ for key in 'REQUEST_URL', 'REQUEST_URI', 'UNENCODED_URL':
+ if key not in environ:
+ continue
+ request_uri = unquote(environ[key])
+ script_name = unquote(environ.get('SCRIPT_NAME', ''))
+ if request_uri.startswith(script_name):
+ environ['PATH_INFO'] = request_uri[len(script_name):] \
+ .split('?', 1)[0]
+ break
+ return self.app(environ, start_response)
+
+
+class ProxyFix(object):
+ """This middleware can be applied to add HTTP proxy support to an
+ application that was not designed with HTTP proxies in mind. It
+ sets `REMOTE_ADDR`, `HTTP_HOST` from `X-Forwarded` headers.
+
+ Do not use this middleware in non-proxy setups for security reasons.
+
+ The original values of `REMOTE_ADDR` and `HTTP_HOST` are stored in
+ the WSGI environment as `werkzeug.proxy_fix.orig_remote_addr` and
+ `werkzeug.proxy_fix.orig_http_host`.
+
+ :param app: the WSGI application
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def get_remote_addr(self, forwarded_for):
+ """Selects the new remote addr from the given list of ips in
+ X-Forwarded-For. By default the first one is picked.
+
+ .. versionadded:: 0.8
+ """
+ if forwarded_for:
+ return forwarded_for[0]
+
+ def __call__(self, environ, start_response):
+ getter = environ.get
+ forwarded_proto = getter('HTTP_X_FORWARDED_PROTO', '')
+ forwarded_for = getter('HTTP_X_FORWARDED_FOR', '').split(',')
+ forwarded_host = getter('HTTP_X_FORWARDED_HOST', '')
+ environ.update({
+ 'werkzeug.proxy_fix.orig_wsgi_url_scheme': getter('wsgi.url_scheme'),
+ 'werkzeug.proxy_fix.orig_remote_addr': getter('REMOTE_ADDR'),
+ 'werkzeug.proxy_fix.orig_http_host': getter('HTTP_HOST')
+ })
+ forwarded_for = [x for x in [x.strip() for x in forwarded_for] if x]
+ remote_addr = self.get_remote_addr(forwarded_for)
+ if remote_addr is not None:
+ environ['REMOTE_ADDR'] = remote_addr
+ if forwarded_host:
+ environ['HTTP_HOST'] = forwarded_host
+ if forwarded_proto:
+ environ['wsgi.url_scheme'] = forwarded_proto
+ return self.app(environ, start_response)
+
+
+class HeaderRewriterFix(object):
+ """This middleware can remove response headers and add others. This
+ is for example useful to remove the `Date` header from responses if you
+ are using a server that adds that header, no matter if it's present or
+ not or to add `X-Powered-By` headers::
+
+ app = HeaderRewriterFix(app, remove_headers=['Date'],
+ add_headers=[('X-Powered-By', 'WSGI')])
+
+ :param app: the WSGI application
+ :param remove_headers: a sequence of header keys that should be
+ removed.
+ :param add_headers: a sequence of ``(key, value)`` tuples that should
+ be added.
+ """
+
+ def __init__(self, app, remove_headers=None, add_headers=None):
+ self.app = app
+ self.remove_headers = set(x.lower() for x in (remove_headers or ()))
+ self.add_headers = list(add_headers or ())
+
+ def __call__(self, environ, start_response):
+ def rewriting_start_response(status, headers, exc_info=None):
+ new_headers = []
+ for key, value in headers:
+ if key.lower() not in self.remove_headers:
+ new_headers.append((key, value))
+ new_headers += self.add_headers
+ return start_response(status, new_headers, exc_info)
+ return self.app(environ, rewriting_start_response)
+
+
+class InternetExplorerFix(object):
+ """This middleware fixes a couple of bugs with Microsoft Internet
+ Explorer. Currently the following fixes are applied:
+
+ - removing of `Vary` headers for unsupported mimetypes which
+ causes troubles with caching. Can be disabled by passing
+ ``fix_vary=False`` to the constructor.
+ see: http://support.microsoft.com/kb/824847/en-us
+
+ - removes offending headers to work around caching bugs in
+ Internet Explorer if `Content-Disposition` is set. Can be
+ disabled by passing ``fix_attach=False`` to the constructor.
+
+ If it does not detect affected Internet Explorer versions it won't touch
+ the request / response.
+ """
+
+ # This code was inspired by Django fixers for the same bugs. The
+ # fix_vary and fix_attach fixers were originally implemented in Django
+ # by Michael Axiak and is available as part of the Django project:
+ # http://code.djangoproject.com/ticket/4148
+
+ def __init__(self, app, fix_vary=True, fix_attach=True):
+ self.app = app
+ self.fix_vary = fix_vary
+ self.fix_attach = fix_attach
+
+ def fix_headers(self, environ, headers, status=None):
+ if self.fix_vary:
+ header = headers.get('content-type', '')
+ mimetype, options = parse_options_header(header)
+ if mimetype not in ('text/html', 'text/plain', 'text/sgml'):
+ headers.pop('vary', None)
+
+ if self.fix_attach and 'content-disposition' in headers:
+ pragma = parse_set_header(headers.get('pragma', ''))
+ pragma.discard('no-cache')
+ header = pragma.to_header()
+ if not header:
+ headers.pop('pragma', '')
+ else:
+ headers['Pragma'] = header
+ header = headers.get('cache-control', '')
+ if header:
+ cc = parse_cache_control_header(header,
+ cls=ResponseCacheControl)
+ cc.no_cache = None
+ cc.no_store = False
+ header = cc.to_header()
+ if not header:
+ headers.pop('cache-control', '')
+ else:
+ headers['Cache-Control'] = header
+
+ def run_fixed(self, environ, start_response):
+ def fixing_start_response(status, headers, exc_info=None):
+ self.fix_headers(environ, Headers.linked(headers), status)
+ return start_response(status, headers, exc_info)
+ return self.app(environ, fixing_start_response)
+
+ def __call__(self, environ, start_response):
+ ua = UserAgent(environ)
+ if ua.browser != 'msie':
+ return self.app(environ, start_response)
+ return self.run_fixed(environ, start_response)
diff --git a/websdk/werkzeug/contrib/iterio.py b/websdk/werkzeug/contrib/iterio.py
new file mode 100644
index 0000000..0718659
--- /dev/null
+++ b/websdk/werkzeug/contrib/iterio.py
@@ -0,0 +1,277 @@
+# -*- coding: utf-8 -*-
+r"""
+ werkzeug.contrib.iterio
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ This module implements a :class:`IterIO` that converts an iterator into
+ a stream object and the other way round. Converting streams into
+ iterators requires the `greenlet`_ module.
+
+ To convert an iterator into a stream all you have to do is to pass it
+ directly to the :class:`IterIO` constructor. In this example we pass it
+ a newly created generator::
+
+ def foo():
+ yield "something\n"
+ yield "otherthings"
+ stream = IterIO(foo())
+ print stream.read() # read the whole iterator
+
+ The other way round works a bit different because we have to ensure that
+ the code execution doesn't take place yet. An :class:`IterIO` call with a
+ callable as first argument does two things. The function itself is passed
+ an :class:`IterIO` stream it can feed. The object returned by the
+ :class:`IterIO` constructor on the other hand is not an stream object but
+ an iterator::
+
+ def foo(stream):
+ stream.write("some")
+ stream.write("thing")
+ stream.flush()
+ stream.write("otherthing")
+ iterator = IterIO(foo)
+ print iterator.next() # prints something
+ print iterator.next() # prints otherthing
+ iterator.next() # raises StopIteration
+
+ .. _greenlet: http://codespeak.net/py/dist/greenlet.html
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+try:
+ import greenlet
+except ImportError:
+ greenlet = None
+
+
+class IterIO(object):
+ """Instances of this object implement an interface compatible with the
+ standard Python :class:`file` object. Streams are either read-only or
+ write-only depending on how the object is created.
+ """
+
+ def __new__(cls, obj):
+ try:
+ iterator = iter(obj)
+ except TypeError:
+ return IterI(obj)
+ return IterO(iterator)
+
+ def __iter__(self):
+ return self
+
+ def tell(self):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ return self.pos
+
+ def isatty(self):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ return False
+
+ def seek(self, pos, mode=0):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ raise IOError(9, 'Bad file descriptor')
+
+ def truncate(self, size=None):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ raise IOError(9, 'Bad file descriptor')
+
+ def write(self, s):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ raise IOError(9, 'Bad file descriptor')
+
+ def writelines(self, list):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ raise IOError(9, 'Bad file descriptor')
+
+ def read(self, n=-1):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ raise IOError(9, 'Bad file descriptor')
+
+ def readlines(self, sizehint=0):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ raise IOError(9, 'Bad file descriptor')
+
+ def readline(self, length=None):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ raise IOError(9, 'Bad file descriptor')
+
+ def flush(self):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ raise IOError(9, 'Bad file descriptor')
+
+ def next(self):
+ if self.closed:
+ raise StopIteration()
+ line = self.readline()
+ if not line:
+ raise StopIteration()
+ return line
+
+
+class IterI(IterIO):
+ """Convert an stream into an iterator."""
+
+ def __new__(cls, func):
+ if greenlet is None:
+ raise RuntimeError('IterI requires greenlet support')
+ stream = object.__new__(cls)
+ stream._parent = greenlet.getcurrent()
+ stream._buffer = []
+ stream.closed = False
+ stream.pos = 0
+
+ def run():
+ func(stream)
+ stream.flush()
+
+ g = greenlet.greenlet(run, stream._parent)
+ while 1:
+ rv = g.switch()
+ if not rv:
+ return
+ yield rv[0]
+
+ def close(self):
+ if not self.closed:
+ self.closed = True
+
+ def write(self, s):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ self.pos += len(s)
+ self._buffer.append(s)
+
+ def writelines(self, list):
+ self.write(''.join(list))
+
+ def flush(self):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ data = ''.join(self._buffer)
+ self._buffer = []
+ self._parent.switch((data,))
+
+
+class IterO(IterIO):
+ """Iter output. Wrap an iterator and give it a stream like interface."""
+
+ def __new__(cls, gen):
+ self = object.__new__(cls)
+ self._gen = gen
+ self._buf = ''
+ self.closed = False
+ self.pos = 0
+ return self
+
+ def __iter__(self):
+ return self
+
+ def close(self):
+ if not self.closed:
+ self.closed = True
+ if hasattr(self._gen, 'close'):
+ self._gen.close()
+
+ def seek(self, pos, mode=0):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ if mode == 1:
+ pos += self.pos
+ elif mode == 2:
+ self.read()
+ self.pos = min(self.pos, self.pos + pos)
+ return
+ elif mode != 0:
+ raise IOError('Invalid argument')
+ buf = []
+ try:
+ tmp_end_pos = len(self._buf)
+ while pos > tmp_end_pos:
+ item = self._gen.next()
+ tmp_end_pos += len(item)
+ buf.append(item)
+ except StopIteration:
+ pass
+ if buf:
+ self._buf += ''.join(buf)
+ self.pos = max(0, pos)
+
+ def read(self, n=-1):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ if n < 0:
+ self._buf += ''.join(self._gen)
+ result = self._buf[self.pos:]
+ self.pos += len(result)
+ return result
+ new_pos = self.pos + n
+ buf = []
+ try:
+ tmp_end_pos = len(self._buf)
+ while new_pos > tmp_end_pos:
+ item = self._gen.next()
+ tmp_end_pos += len(item)
+ buf.append(item)
+ except StopIteration:
+ pass
+ if buf:
+ self._buf += ''.join(buf)
+ new_pos = max(0, new_pos)
+ try:
+ return self._buf[self.pos:new_pos]
+ finally:
+ self.pos = min(new_pos, len(self._buf))
+
+ def readline(self, length=None):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ nl_pos = self._buf.find('\n', self.pos)
+ buf = []
+ try:
+ pos = self.pos
+ while nl_pos < 0:
+ item = self._gen.next()
+ local_pos = item.find('\n')
+ buf.append(item)
+ if local_pos >= 0:
+ nl_pos = pos + local_pos
+ break
+ pos += len(item)
+ except StopIteration:
+ pass
+ if buf:
+ self._buf += ''.join(buf)
+ if nl_pos < 0:
+ new_pos = len(self._buf)
+ else:
+ new_pos = nl_pos + 1
+ if length is not None and self.pos + length < new_pos:
+ new_pos = self.pos + length
+ try:
+ return self._buf[self.pos:new_pos]
+ finally:
+ self.pos = min(new_pos, len(self._buf))
+
+ def readlines(self, sizehint=0):
+ total = 0
+ lines = []
+ line = self.readline()
+ while line:
+ lines.append(line)
+ total += len(line)
+ if 0 < sizehint <= total:
+ break
+ line = self.readline()
+ return lines
diff --git a/websdk/werkzeug/contrib/jsrouting.py b/websdk/werkzeug/contrib/jsrouting.py
new file mode 100644
index 0000000..9b7d0c0
--- /dev/null
+++ b/websdk/werkzeug/contrib/jsrouting.py
@@ -0,0 +1,259 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib.jsrouting
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Addon module that allows to create a JavaScript function from a map
+ that generates rules.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+try:
+ from simplejson import dumps
+except ImportError:
+ try:
+ from json import dumps
+ except ImportError:
+ def dumps(*args):
+ raise RuntimeError('simplejson required for jsrouting')
+
+from inspect import getmro
+from werkzeug.routing import NumberConverter
+
+
+def render_template(name_parts, rules, converters):
+ result = u''
+ if name_parts:
+ for idx in xrange(0, len(name_parts) - 1):
+ name = u'.'.join(name_parts[:idx + 1])
+ result += u"if (typeof %s === 'undefined') %s = {}\n" % (name, name)
+ result += '%s = ' % '.'.join(name_parts)
+ result += """(function (server_name, script_name, subdomain, url_scheme) {
+ var converters = %(converters)s;
+ var rules = $rules;
+ function in_array(array, value) {
+ if (array.indexOf != undefined) {
+ return array.indexOf(value) != -1;
+ }
+ for (var i = 0; i < array.length; i++) {
+ if (array[i] == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+ function array_diff(array1, array2) {
+ array1 = array1.slice();
+ for (var i = array1.length-1; i >= 0; i--) {
+ if (in_array(array2, array1[i])) {
+ array1.splice(i, 1);
+ }
+ }
+ return array1;
+ }
+ function split_obj(obj) {
+ var names = [];
+ var values = [];
+ for (var name in obj) {
+ if (typeof(obj[name]) != 'function') {
+ names.push(name);
+ values.push(obj[name]);
+ }
+ }
+ return {names: names, values: values, original: obj};
+ }
+ function suitable(rule, args) {
+ var default_args = split_obj(rule.defaults || {});
+ var diff_arg_names = array_diff(rule.arguments, default_args.names);
+
+ for (var i = 0; i < diff_arg_names.length; i++) {
+ if (!in_array(args.names, diff_arg_names[i])) {
+ return false;
+ }
+ }
+
+ if (array_diff(rule.arguments, args.names).length == 0) {
+ if (rule.defaults == null) {
+ return true;
+ }
+ for (var i = 0; i < default_args.names.length; i++) {
+ var key = default_args.names[i];
+ var value = default_args.values[i];
+ if (value != args.original[key]) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+ function build(rule, args) {
+ var tmp = [];
+ var processed = rule.arguments.slice();
+ for (var i = 0; i < rule.trace.length; i++) {
+ var part = rule.trace[i];
+ if (part.is_dynamic) {
+ var converter = converters[rule.converters[part.data]];
+ var data = converter(args.original[part.data]);
+ if (data == null) {
+ return null;
+ }
+ tmp.push(data);
+ processed.push(part.name);
+ } else {
+ tmp.push(part.data);
+ }
+ }
+ tmp = tmp.join('');
+ var pipe = tmp.indexOf('|');
+ var subdomain = tmp.substring(0, pipe);
+ var url = tmp.substring(pipe+1);
+
+ var unprocessed = array_diff(args.names, processed);
+ var first_query_var = true;
+ for (var i = 0; i < unprocessed.length; i++) {
+ if (first_query_var) {
+ url += '?';
+ } else {
+ url += '&';
+ }
+ first_query_var = false;
+ url += encodeURIComponent(unprocessed[i]);
+ url += '=';
+ url += encodeURIComponent(args.original[unprocessed[i]]);
+ }
+ return {subdomain: subdomain, path: url};
+ }
+ function lstrip(s, c) {
+ while (s && s.substring(0, 1) == c) {
+ s = s.substring(1);
+ }
+ return s;
+ }
+ function rstrip(s, c) {
+ while (s && s.substring(s.length-1, s.length) == c) {
+ s = s.substring(0, s.length-1);
+ }
+ return s;
+ }
+ return function(endpoint, args, force_external) {
+ args = split_obj(args);
+ var rv = null;
+ for (var i = 0; i < rules.length; i++) {
+ var rule = rules[i];
+ if (rule.endpoint != endpoint) continue;
+ if (suitable(rule, args)) {
+ rv = build(rule, args);
+ if (rv != null) {
+ break;
+ }
+ }
+ }
+ if (rv == null) {
+ return null;
+ }
+ if (!force_external && rv.subdomain == subdomain) {
+ return rstrip(script_name, '/') + '/' + lstrip(rv.path, '/');
+ } else {
+ return url_scheme + '://'
+ + (rv.subdomain ? rv.subdomain + '.' : '')
+ + server_name + rstrip(script_name, '/')
+ + '/' + lstrip(rv.path, '/');
+ }
+ };
+})""" % {'converters': u', '.join(converters)}
+ return result
+
+
+def generate_map(map, name='url_map'):
+ """
+ Generates a JavaScript function containing the rules defined in
+ this map, to be used with a MapAdapter's generate_javascript
+ method. If you don't pass a name the returned JavaScript code is
+ an expression that returns a function. Otherwise it's a standalone
+ script that assigns the function with that name. Dotted names are
+ resolved (so you an use a name like 'obj.url_for')
+
+ In order to use JavaScript generation, simplejson must be installed.
+
+ Note that using this feature will expose the rules
+ defined in your map to users. If your rules contain sensitive
+ information, don't use JavaScript generation!
+ """
+ map.update()
+ rules = []
+ converters = []
+ for rule in map.iter_rules():
+ trace = [{
+ 'is_dynamic': is_dynamic,
+ 'data': data
+ } for is_dynamic, data in rule._trace]
+ rule_converters = {}
+ for key, converter in rule._converters.iteritems():
+ js_func = js_to_url_function(converter)
+ try:
+ index = converters.index(js_func)
+ except ValueError:
+ converters.append(js_func)
+ index = len(converters) - 1
+ rule_converters[key] = index
+ rules.append({
+ u'endpoint': rule.endpoint,
+ u'arguments': list(rule.arguments),
+ u'converters': rule_converters,
+ u'trace': trace,
+ u'defaults': rule.defaults
+ })
+
+ return render_template(name_parts=name and name.split('.') or [],
+ rules=dumps(rules),
+ converters=converters)
+
+
+def generate_adapter(adapter, name='url_for', map_name='url_map'):
+ """Generates the url building function for a map."""
+ values = {
+ u'server_name': dumps(adapter.server_name),
+ u'script_name': dumps(adapter.script_name),
+ u'subdomain': dumps(adapter.subdomain),
+ u'url_scheme': dumps(adapter.url_scheme),
+ u'name': name,
+ u'map_name': map_name
+ }
+ return u'''\
+var %(name)s = %(map_name)s(
+ %(server_name)s,
+ %(script_name)s,
+ %(subdomain)s,
+ %(url_scheme)s
+);''' % values
+
+
+def js_to_url_function(converter):
+ """Get the JavaScript converter function from a rule."""
+ if hasattr(converter, 'js_to_url_function'):
+ data = converter.js_to_url_function()
+ else:
+ for cls in getmro(type(converter)):
+ if cls in js_to_url_functions:
+ data = js_to_url_functions[cls](converter)
+ break
+ else:
+ return 'encodeURIComponent'
+ return '(function(value) { %s })' % data
+
+
+def NumberConverter_js_to_url(conv):
+ if conv.fixed_digits:
+ return u'''\
+var result = value.toString();
+while (result.length < %s)
+ result = '0' + result;
+return result;''' % conv.fixed_digits
+ return u'return value.toString();'
+
+
+js_to_url_functions = {
+ NumberConverter: NumberConverter_js_to_url
+}
diff --git a/websdk/werkzeug/contrib/kickstart.py b/websdk/werkzeug/contrib/kickstart.py
new file mode 100644
index 0000000..43c0e7c
--- /dev/null
+++ b/websdk/werkzeug/contrib/kickstart.py
@@ -0,0 +1,288 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib.kickstart
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ This module provides some simple shortcuts to make using Werkzeug simpler
+ for small scripts.
+
+ These improvements include predefined `Request` and `Response` objects as
+ well as a predefined `Application` object which can be customized in child
+ classes, of course. The `Request` and `Reponse` objects handle URL
+ generation as well as sessions via `werkzeug.contrib.sessions` and are
+ purely optional.
+
+ There is also some integration of template engines. The template loaders
+ are, of course, not neccessary to use the template engines in Werkzeug,
+ but they provide a common interface. Currently supported template engines
+ include Werkzeug's minitmpl and Genshi_. Support for other engines can be
+ added in a trivial way. These loaders provide a template interface
+ similar to the one used by Django_.
+
+ .. _Genshi: http://genshi.edgewall.org/
+ .. _Django: http://www.djangoproject.com/
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+from os import path
+from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase
+from werkzeug.templates import Template
+from werkzeug.exceptions import HTTPException
+from werkzeug.routing import RequestRedirect
+
+__all__ = ['Request', 'Response', 'TemplateNotFound', 'TemplateLoader',
+ 'GenshiTemplateLoader', 'Application']
+
+from warnings import warn
+warn(DeprecationWarning('werkzeug.contrib.kickstart is deprecated and '
+ 'will be removed in Werkzeug 1.0'))
+
+
+class Request(RequestBase):
+ """A handy subclass of the base request that adds a URL builder.
+ It when supplied a session store, it is also able to handle sessions.
+ """
+
+ def __init__(self, environ, url_map,
+ session_store=None, cookie_name=None):
+ # call the parent for initialization
+ RequestBase.__init__(self, environ)
+ # create an adapter
+ self.url_adapter = url_map.bind_to_environ(environ)
+ # create all stuff for sessions
+ self.session_store = session_store
+ self.cookie_name = cookie_name
+
+ if session_store is not None and cookie_name is not None:
+ if cookie_name in self.cookies:
+ # get the session out of the storage
+ self.session = session_store.get(self.cookies[cookie_name])
+ else:
+ # create a new session
+ self.session = session_store.new()
+
+ def url_for(self, callback, **values):
+ return self.url_adapter.build(callback, values)
+
+
+class Response(ResponseBase):
+ """
+ A subclass of base response which sets the default mimetype to text/html.
+ It the `Request` that came in is using Werkzeug sessions, this class
+ takes care of saving that session.
+ """
+ default_mimetype = 'text/html'
+
+ def __call__(self, environ, start_response):
+ # get the request object
+ request = environ['werkzeug.request']
+
+ if request.session_store is not None:
+ # save the session if neccessary
+ request.session_store.save_if_modified(request.session)
+
+ # set the cookie for the browser if it is not there:
+ if request.cookie_name not in request.cookies:
+ self.set_cookie(request.cookie_name, request.session.sid)
+
+ # go on with normal response business
+ return ResponseBase.__call__(self, environ, start_response)
+
+
+class Processor(object):
+ """A request and response processor - it is what Django calls a
+ middleware, but Werkzeug also includes straight-foward support for real
+ WSGI middlewares, so another name was chosen.
+
+ The code of this processor is derived from the example in the Werkzeug
+ trac, called `Request and Response Processor
+ <http://dev.pocoo.org/projects/werkzeug/wiki/RequestResponseProcessor>`_
+ """
+
+ def process_request(self, request):
+ return request
+
+ def process_response(self, request, response):
+ return response
+
+ def process_view(self, request, view_func, view_args, view_kwargs):
+ """process_view() is called just before the Application calls the
+ function specified by view_func.
+
+ If this returns None, the Application processes the next Processor,
+ and if it returns something else (like a Response instance), that
+ will be returned without any further processing.
+ """
+ return None
+
+ def process_exception(self, request, exception):
+ return None
+
+
+class Application(object):
+ """A generic WSGI application which can be used to start with Werkzeug in
+ an easy, straightforward way.
+ """
+
+ def __init__(self, name, url_map, session=False, processors=None):
+ # save the name and the URL-map, as it'll be needed later on
+ self.name = name
+ self.url_map = url_map
+ # save the list of processors if supplied
+ self.processors = processors or []
+ # create an instance of the storage
+ if session:
+ self.store = session
+ else:
+ self.store = None
+
+ def __call__(self, environ, start_response):
+ # create a request - with or without session support
+ if self.store is not None:
+ request = Request(environ, self.url_map,
+ session_store=self.store, cookie_name='%s_sid' % self.name)
+ else:
+ request = Request(environ, self.url_map)
+
+ # apply the request processors
+ for processor in self.processors:
+ request = processor.process_request(request)
+
+ try:
+ # find the callback to which the URL is mapped
+ callback, args = request.url_adapter.match(request.path)
+ except (HTTPException, RequestRedirect), e:
+ response = e
+ else:
+ # check all view processors
+ for processor in self.processors:
+ action = processor.process_view(request, callback, (), args)
+ if action is not None:
+ # it is overriding the default behaviour, this is
+ # short-circuiting the processing, so it returns here
+ return action(environ, start_response)
+
+ try:
+ response = callback(request, **args)
+ except Exception, exception:
+ # the callback raised some exception, need to process that
+ for processor in reversed(self.processors):
+ # filter it through the exception processor
+ action = processor.process_exception(request, exception)
+ if action is not None:
+ # the exception processor returned some action
+ return action(environ, start_response)
+ # still not handled by a exception processor, so re-raise
+ raise
+
+ # apply the response processors
+ for processor in reversed(self.processors):
+ response = processor.process_response(request, response)
+
+ # return the completely processed response
+ return response(environ, start_response)
+
+
+ def config_session(self, store, expiration='session'):
+ """
+ Configures the setting for cookies. You can also disable cookies by
+ setting store to None.
+ """
+ self.store = store
+ # expiration=session is the default anyway
+ # TODO: add settings to define the expiration date, the domain, the
+ # path any maybe the secure parameter.
+
+
+class TemplateNotFound(IOError, LookupError):
+ """
+ A template was not found by the template loader.
+ """
+
+ def __init__(self, name):
+ IOError.__init__(self, name)
+ self.name = name
+
+
+class TemplateLoader(object):
+ """
+ A simple loader interface for the werkzeug minitmpl
+ template language.
+ """
+
+ def __init__(self, search_path, encoding='utf-8'):
+ self.search_path = path.abspath(search_path)
+ self.encoding = encoding
+
+ def get_template(self, name):
+ """Get a template from a given name."""
+ filename = path.join(self.search_path, *[p for p in name.split('/')
+ if p and p[0] != '.'])
+ if not path.exists(filename):
+ raise TemplateNotFound(name)
+ return Template.from_file(filename, self.encoding)
+
+ def render_to_response(self, *args, **kwargs):
+ """Load and render a template into a response object."""
+ return Response(self.render_to_string(*args, **kwargs))
+
+ def render_to_string(self, *args, **kwargs):
+ """Load and render a template into a unicode string."""
+ try:
+ template_name, args = args[0], args[1:]
+ except IndexError:
+ raise TypeError('name of template required')
+ return self.get_template(template_name).render(*args, **kwargs)
+
+
+class GenshiTemplateLoader(TemplateLoader):
+ """A unified interface for loading Genshi templates. Actually a quite thin
+ wrapper for Genshi's TemplateLoader.
+
+ It sets some defaults that differ from the Genshi loader, most notably
+ auto_reload is active. All imporant options can be passed through to
+ Genshi.
+ The default output type is 'html', but can be adjusted easily by changing
+ the `output_type` attribute.
+ """
+ def __init__(self, search_path, encoding='utf-8', **kwargs):
+ TemplateLoader.__init__(self, search_path, encoding)
+ # import Genshi here, because we don't want a general Genshi
+ # dependency, only a local one
+ from genshi.template import TemplateLoader as GenshiLoader
+ from genshi.template.loader import TemplateNotFound
+
+ self.not_found_exception = TemplateNotFound
+ # set auto_reload to True per default
+ reload_template = kwargs.pop('auto_reload', True)
+ # get rid of default_encoding as this template loaders overwrites it
+ # with the value of encoding
+ kwargs.pop('default_encoding', None)
+
+ # now, all arguments are clean, pass them on
+ self.loader = GenshiLoader(search_path, default_encoding=encoding,
+ auto_reload=reload_template, **kwargs)
+
+ # the default output is HTML but can be overridden easily
+ self.output_type = 'html'
+ self.encoding = encoding
+
+ def get_template(self, template_name):
+ """Get the template which is at the given name"""
+ try:
+ return self.loader.load(template_name, encoding=self.encoding)
+ except self.not_found_exception, e:
+ # catch the exception raised by Genshi, convert it into a werkzeug
+ # exception (for the sake of consistency)
+ raise TemplateNotFound(template_name)
+
+ def render_to_string(self, template_name, context=None):
+ """Load and render a template into an unicode string"""
+ # create an empty context if no context was specified
+ context = context or {}
+ tmpl = self.get_template(template_name)
+ # render the template into a unicode string (None means unicode)
+ return tmpl. \
+ generate(**context). \
+ render(self.output_type, encoding=None)
diff --git a/websdk/werkzeug/contrib/limiter.py b/websdk/werkzeug/contrib/limiter.py
new file mode 100644
index 0000000..d0dbcc5
--- /dev/null
+++ b/websdk/werkzeug/contrib/limiter.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib.limiter
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ A middleware that limits incoming data. This works around problems with
+ Trac_ or Django_ because those directly stream into the memory.
+
+ .. _Trac: http://trac.edgewall.org/
+ .. _Django: http://www.djangoproject.com/
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+from warnings import warn
+
+from werkzeug.wsgi import LimitedStream
+
+
+class StreamLimitMiddleware(object):
+ """Limits the input stream to a given number of bytes. This is useful if
+ you have a WSGI application that reads form data into memory (django for
+ example) and you don't want users to harm the server by uploading tons of
+ data.
+
+ Default is 10MB
+ """
+
+ def __init__(self, app, maximum_size=1024 * 1024 * 10):
+ self.app = app
+ self.maximum_size = maximum_size
+
+ def __call__(self, environ, start_response):
+ limit = min(self.maximum_size, int(environ.get('CONTENT_LENGTH') or 0))
+ environ['wsgi.input'] = LimitedStream(environ['wsgi.input'], limit)
+ return self.app(environ, start_response)
diff --git a/websdk/werkzeug/contrib/lint.py b/websdk/werkzeug/contrib/lint.py
new file mode 100644
index 0000000..c7adff9
--- /dev/null
+++ b/websdk/werkzeug/contrib/lint.py
@@ -0,0 +1,333 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib.lint
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ .. versionadded:: 0.5
+
+ This module provides a middleware that performs sanity checks of the WSGI
+ application. It checks that :pep:`333` is properly implemented and warns
+ on some common HTTP errors such as non-empty responses for 304 status
+ codes.
+
+ This module provides a middleware, the :class:`LintMiddleware`. Wrap your
+ application with it and it will warn about common problems with WSGI and
+ HTTP while your application is running.
+
+ It's strongly recommended to use it during development.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+from urlparse import urlparse
+from warnings import warn
+
+from werkzeug.datastructures import Headers
+from werkzeug.http import is_entity_header
+from werkzeug.wsgi import FileWrapper
+
+
+class WSGIWarning(Warning):
+ """Warning class for WSGI warnings."""
+
+
+class HTTPWarning(Warning):
+ """Warning class for HTTP warnings."""
+
+
+def check_string(context, obj, stacklevel=3):
+ if type(obj) is not str:
+ warn(WSGIWarning('%s requires bytestrings, got %s' %
+ (context, obj.__class__.__name__)))
+
+
+class InputStream(object):
+
+ def __init__(self, stream):
+ self._stream = stream
+
+ def read(self, *args):
+ if len(args) == 0:
+ warn(WSGIWarning('wsgi does not guarantee an EOF marker on the '
+ 'input stream, thus making calls to '
+ 'wsgi.input.read() unsafe. Conforming servers '
+ 'may never return from this call.'),
+ stacklevel=2)
+ elif len(args) != 1:
+ warn(WSGIWarning('too many parameters passed to wsgi.input.read()'),
+ stacklevel=2)
+ return self._stream.read(*args)
+
+ def readline(self, *args):
+ if len(args) == 0:
+ warn(WSGIWarning('Calls to wsgi.input.readline() without arguments'
+ ' are unsafe. Use wsgi.input.read() instead.'),
+ stacklevel=2)
+ elif len(args) == 1:
+ warn(WSGIWarning('wsgi.input.readline() was called with a size hint. '
+ 'WSGI does not support this, although it\'s available '
+ 'on all major servers.'),
+ stacklevel=2)
+ else:
+ raise TypeError('too many arguments passed to wsgi.input.readline()')
+ return self._stream.readline(*args)
+
+ def __iter__(self):
+ try:
+ return iter(self._stream)
+ except TypeError:
+ warn(WSGIWarning('wsgi.input is not iterable.'), stacklevel=2)
+ return iter(())
+
+ def close(self):
+ warn(WSGIWarning('application closed the input stream!'),
+ stacklevel=2)
+ self._stream.close()
+
+
+class ErrorStream(object):
+
+ def __init__(self, stream):
+ self._stream = stream
+
+ def write(self, s):
+ check_string('wsgi.error.write()', s)
+ self._stream.write(s)
+
+ def flush(self):
+ self._stream.flush()
+
+ def writelines(self, seq):
+ for line in seq:
+ self.write(seq)
+
+ def close(self):
+ warn(WSGIWarning('application closed the error stream!'),
+ stacklevel=2)
+ self._stream.close()
+
+
+class GuardedWrite(object):
+
+ def __init__(self, write, chunks):
+ self._write = write
+ self._chunks = chunks
+
+ def __call__(self, s):
+ check_string('write()', s)
+ self._write.write(s)
+ self._chunks.append(len(s))
+
+
+class GuardedIterator(object):
+
+ def __init__(self, iterator, headers_set, chunks):
+ self._iterator = iterator
+ self._next = iter(iterator).next
+ self.closed = False
+ self.headers_set = headers_set
+ self.chunks = chunks
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ if self.closed:
+ warn(WSGIWarning('iterated over closed app_iter'),
+ stacklevel=2)
+ rv = self._next()
+ if not self.headers_set:
+ warn(WSGIWarning('Application returned before it '
+ 'started the response'), stacklevel=2)
+ check_string('application iterator items', rv)
+ self.chunks.append(len(rv))
+ return rv
+
+ def close(self):
+ self.closed = True
+ if hasattr(self._iterator, 'close'):
+ self._iterator.close()
+
+ if self.headers_set:
+ status_code, headers = self.headers_set
+ bytes_sent = sum(self.chunks)
+ content_length = headers.get('content-length', type=int)
+
+ if status_code == 304:
+ for key, value in headers:
+ key = key.lower()
+ if key not in ('expires', 'content-location') and \
+ is_entity_header(key):
+ warn(HTTPWarning('entity header %r found in 304 '
+ 'response' % key))
+ if bytes_sent:
+ warn(HTTPWarning('304 responses must not have a body'))
+ elif 100 <= status_code < 200 or status_code == 204:
+ if content_length != 0:
+ warn(HTTPWarning('%r responses must have an empty '
+ 'content length') % status_code)
+ if bytes_sent:
+ warn(HTTPWarning('%r responses must not have a body' %
+ status_code))
+ elif content_length is not None and content_length != bytes_sent:
+ warn(WSGIWarning('Content-Length and the number of bytes '
+ 'sent to the client do not match.'))
+
+ def __del__(self):
+ if not self.closed:
+ try:
+ warn(WSGIWarning('Iterator was garbage collected before '
+ 'it was closed.'))
+ except Exception:
+ pass
+
+
+class LintMiddleware(object):
+ """This middleware wraps an application and warns on common errors.
+ Among other thing it currently checks for the following problems:
+
+ - invalid status codes
+ - non-bytestrings sent to the WSGI server
+ - strings returned from the WSGI application
+ - non-empty conditional responses
+ - unquoted etags
+ - relative URLs in the Location header
+ - unsafe calls to wsgi.input
+ - unclosed iterators
+
+ Detected errors are emitted using the standard Python :mod:`warnings`
+ system and usually end up on :data:`stderr`.
+
+ ::
+
+ from werkzeug.contrib.lint import LintMiddleware
+ app = LintMiddleware(app)
+
+ :param app: the application to wrap
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def check_environ(self, environ):
+ if type(environ) is not dict:
+ warn(WSGIWarning('WSGI environment is not a standard python dict.'),
+ stacklevel=4)
+ for key in ('REQUEST_METHOD', 'SERVER_NAME', 'SERVER_PORT',
+ 'wsgi.version', 'wsgi.input', 'wsgi.errors',
+ 'wsgi.multithread', 'wsgi.multiprocess',
+ 'wsgi.run_once'):
+ if key not in environ:
+ warn(WSGIWarning('required environment key %r not found'
+ % key), stacklevel=3)
+ if environ['wsgi.version'] != (1, 0):
+ warn(WSGIWarning('environ is not a WSGI 1.0 environ'),
+ stacklevel=3)
+
+ script_name = environ.get('SCRIPT_NAME', '')
+ if script_name and script_name[:1] != '/':
+ warn(WSGIWarning('SCRIPT_NAME does not start with a slash: %r'
+ % script_name), stacklevel=3)
+ path_info = environ.get('PATH_INFO', '')
+ if path_info[:1] != '/':
+ warn(WSGIWarning('PATH_INFO does not start with a slash: %r'
+ % path_info), stacklevel=3)
+
+
+ def check_start_response(self, status, headers, exc_info):
+ check_string('status', status)
+ status_code = status.split(None, 1)[0]
+ if len(status_code) != 3 or not status_code.isdigit():
+ warn(WSGIWarning('Status code must be three digits'), stacklevel=3)
+ if len(status) < 4 or status[3] != ' ':
+ warn(WSGIWarning('Invalid value for status %r. Valid '
+ 'status strings are three digits, a space '
+ 'and a status explanation'), stacklevel=3)
+ status_code = int(status_code)
+ if status_code < 100:
+ warn(WSGIWarning('status code < 100 detected'), stacklevel=3)
+
+ if type(headers) is not list:
+ warn(WSGIWarning('header list is not a list'), stacklevel=3)
+ for item in headers:
+ if type(item) is not tuple or len(item) != 2:
+ warn(WSGIWarning('Headers must tuple 2-item tuples'),
+ stacklevel=3)
+ name, value = item
+ if type(name) is not str or type(value) is not str:
+ warn(WSGIWarning('header items must be strings'),
+ stacklevel=3)
+ if name.lower() == 'status':
+ warn(WSGIWarning('The status header is not supported due to '
+ 'conflicts with the CGI spec.'),
+ stacklevel=3)
+
+ if exc_info is not None and not isinstance(exc_info, tuple):
+ warn(WSGIWarning('invalid value for exc_info'), stacklevel=3)
+
+ headers = Headers(headers)
+ self.check_headers(headers)
+
+ return status_code, headers
+
+ def check_headers(self, headers):
+ etag = headers.get('etag')
+ if etag is not None:
+ if etag.startswith('w/'):
+ etag = etag[2:]
+ if not (etag[:1] == etag[-1:] == '"'):
+ warn(HTTPWarning('unquoted etag emitted.'), stacklevel=4)
+
+ location = headers.get('location')
+ if location is not None:
+ if not urlparse(location).netloc:
+ warn(HTTPWarning('absolute URLs required for location header'),
+ stacklevel=4)
+
+ def check_iterator(self, app_iter):
+ if isinstance(app_iter, basestring):
+ warn(WSGIWarning('application returned string. Response will '
+ 'send character for character to the client '
+ 'which will kill the performance. Return a '
+ 'list or iterable instead.'), stacklevel=3)
+
+ def __call__(self, *args, **kwargs):
+ if len(args) != 2:
+ warn(WSGIWarning('Two arguments to WSGI app required'), stacklevel=2)
+ if kwargs:
+ warn(WSGIWarning('No keyword arguments to WSGI app allowed'),
+ stacklevel=2)
+ environ, start_response = args
+
+ self.check_environ(environ)
+ environ['wsgi.input'] = InputStream(environ['wsgi.input'])
+ environ['wsgi.errors'] = ErrorStream(environ['wsgi.errors'])
+
+ # hook our own file wrapper in so that applications will always
+ # iterate to the end and we can check the content length
+ environ['wsgi.file_wrapper'] = FileWrapper
+
+ headers_set = []
+ chunks = []
+
+ def checking_start_response(*args, **kwargs):
+ if len(args) not in (2, 3):
+ warn(WSGIWarning('Invalid number of arguments: %s, expected '
+ '2 or 3' % len(args), stacklevel=2))
+ if kwargs:
+ warn(WSGIWarning('no keyword arguments allowed.'))
+
+ status, headers = args[:2]
+ if len(args) == 3:
+ exc_info = args[2]
+ else:
+ exc_info = None
+
+ headers_set[:] = self.check_start_response(status, headers,
+ exc_info)
+ return GuardedWrite(start_response(status, headers, exc_info),
+ chunks)
+
+ app_iter = self.app(environ, checking_start_response)
+ self.check_iterator(app_iter)
+ return GuardedIterator(app_iter, headers_set, chunks)
diff --git a/websdk/werkzeug/contrib/profiler.py b/websdk/werkzeug/contrib/profiler.py
new file mode 100644
index 0000000..58d1465
--- /dev/null
+++ b/websdk/werkzeug/contrib/profiler.py
@@ -0,0 +1,118 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib.profiler
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ This module provides a simple WSGI profiler middleware for finding
+ bottlenecks in web application. It uses the :mod:`profile` or
+ :mod:`cProfile` module to do the profiling and writes the stats to the
+ stream provided (defaults to stderr).
+
+ Example usage::
+
+ from werkzeug.contrib.profiler import ProfilerMiddleware
+ app = ProfilerMiddleware(app)
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+try:
+ try:
+ from cProfile import Profile
+ except ImportError:
+ from profile import Profile
+ from pstats import Stats
+ available = True
+except ImportError:
+ available = False
+
+
+class MergeStream(object):
+ """An object that redirects `write` calls to multiple streams.
+ Use this to log to both `sys.stdout` and a file::
+
+ f = open('profiler.log', 'w')
+ stream = MergeStream(sys.stdout, f)
+ profiler = ProfilerMiddleware(app, stream)
+ """
+
+ def __init__(self, *streams):
+ if not streams:
+ raise TypeError('at least one stream must be given')
+ self.streams = streams
+
+ def write(self, data):
+ for stream in self.streams:
+ stream.write(data)
+
+
+class ProfilerMiddleware(object):
+ """Simple profiler middleware. Wraps a WSGI application and profiles
+ a request. This intentionally buffers the response so that timings are
+ more exact.
+
+ For the exact meaning of `sort_by` and `restrictions` consult the
+ :mod:`profile` documentation.
+
+ :param app: the WSGI application to profile.
+ :param stream: the stream for the profiled stats. defaults to stderr.
+ :param sort_by: a tuple of columns to sort the result by.
+ :param restrictions: a tuple of profiling strictions.
+ """
+
+ def __init__(self, app, stream=None,
+ sort_by=('time', 'calls'), restrictions=()):
+ if not available:
+ raise RuntimeError('the profiler is not available because '
+ 'profile or pstat is not installed.')
+ self._app = app
+ self._stream = stream or sys.stdout
+ self._sort_by = sort_by
+ self._restrictions = restrictions
+
+ def __call__(self, environ, start_response):
+ response_body = []
+
+ def catching_start_response(status, headers, exc_info=None):
+ start_response(status, headers, exc_info)
+ return response_body.append
+
+ def runapp():
+ appiter = self._app(environ, catching_start_response)
+ response_body.extend(appiter)
+ if hasattr(appiter, 'close'):
+ appiter.close()
+
+ p = Profile()
+ p.runcall(runapp)
+ body = ''.join(response_body)
+ stats = Stats(p, stream=self._stream)
+ stats.sort_stats(*self._sort_by)
+
+ self._stream.write('-' * 80)
+ self._stream.write('\nPATH: %r\n' % environ.get('PATH_INFO'))
+ stats.print_stats(*self._restrictions)
+ self._stream.write('-' * 80 + '\n\n')
+
+ return [body]
+
+
+def make_action(app_factory, hostname='localhost', port=5000,
+ threaded=False, processes=1, stream=None,
+ sort_by=('time', 'calls'), restrictions=()):
+ """Return a new callback for :mod:`werkzeug.script` that starts a local
+ server with the profiler enabled.
+
+ ::
+
+ from werkzeug.contrib import profiler
+ action_profile = profiler.make_action(make_app)
+ """
+ def action(hostname=('h', hostname), port=('p', port),
+ threaded=threaded, processes=processes):
+ """Start a new development server."""
+ from werkzeug.serving import run_simple
+ app = ProfilerMiddleware(app_factory(), stream, sort_by, restrictions)
+ run_simple(hostname, port, app, False, None, threaded, processes)
+ return action
diff --git a/websdk/werkzeug/contrib/securecookie.py b/websdk/werkzeug/contrib/securecookie.py
new file mode 100644
index 0000000..9e6feeb
--- /dev/null
+++ b/websdk/werkzeug/contrib/securecookie.py
@@ -0,0 +1,332 @@
+# -*- coding: utf-8 -*-
+r"""
+ werkzeug.contrib.securecookie
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ This module implements a cookie that is not alterable from the client
+ because it adds a checksum the server checks for. You can use it as
+ session replacement if all you have is a user id or something to mark
+ a logged in user.
+
+ Keep in mind that the data is still readable from the client as a
+ normal cookie is. However you don't have to store and flush the
+ sessions you have at the server.
+
+ Example usage:
+
+ >>> from werkzeug.contrib.securecookie import SecureCookie
+ >>> x = SecureCookie({"foo": 42, "baz": (1, 2, 3)}, "deadbeef")
+
+ Dumping into a string so that one can store it in a cookie:
+
+ >>> value = x.serialize()
+
+ Loading from that string again:
+
+ >>> x = SecureCookie.unserialize(value, "deadbeef")
+ >>> x["baz"]
+ (1, 2, 3)
+
+ If someone modifies the cookie and the checksum is wrong the unserialize
+ method will fail silently and return a new empty `SecureCookie` object.
+
+ Keep in mind that the values will be visible in the cookie so do not
+ store data in a cookie you don't want the user to see.
+
+ Application Integration
+ =======================
+
+ If you are using the werkzeug request objects you could integrate the
+ secure cookie into your application like this::
+
+ from werkzeug.utils import cached_property
+ from werkzeug.wrappers import BaseRequest
+ from werkzeug.contrib.securecookie import SecureCookie
+
+ # don't use this key but a different one; you could just use
+ # os.urandom(20) to get something random
+ SECRET_KEY = '\xfa\xdd\xb8z\xae\xe0}4\x8b\xea'
+
+ class Request(BaseRequest):
+
+ @cached_property
+ def client_session(self):
+ data = self.cookies.get('session_data')
+ if not data:
+ return SecureCookie(secret_key=SECRET_KEY)
+ return SecureCookie.unserialize(data, SECRET_KEY)
+
+ def application(environ, start_response):
+ request = Request(environ, start_response)
+
+ # get a response object here
+ response = ...
+
+ if request.client_session.should_save:
+ session_data = request.client_session.serialize()
+ response.set_cookie('session_data', session_data,
+ httponly=True)
+ return response(environ, start_response)
+
+ A less verbose integration can be achieved by using shorthand methods::
+
+ class Request(BaseRequest):
+
+ @cached_property
+ def client_session(self):
+ return SecureCookie.load_cookie(self, secret_key=COOKIE_SECRET)
+
+ def application(environ, start_response):
+ request = Request(environ, start_response)
+
+ # get a response object here
+ response = ...
+
+ request.client_session.save_cookie(response)
+ return response(environ, start_response)
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+import cPickle as pickle
+from hmac import new as hmac
+from time import time
+from werkzeug.urls import url_quote_plus, url_unquote_plus
+from werkzeug._internal import _date_to_unix
+from werkzeug.contrib.sessions import ModificationTrackingDict
+from werkzeug.security import safe_str_cmp
+
+
+# rather ugly way to import the correct hash method. Because
+# hmac either accepts modules with a new method (sha, md5 etc.)
+# or a hashlib factory function we have to figure out what to
+# pass to it. If we have 2.5 or higher (so not 2.4 with a
+# custom hashlib) we import from hashlib and fail if it does
+# not exist (have seen that in old OS X versions).
+# in all other cases the now deprecated sha module is used.
+_default_hash = None
+if sys.version_info >= (2, 5):
+ try:
+ from hashlib import sha1 as _default_hash
+ except ImportError:
+ pass
+if _default_hash is None:
+ import sha as _default_hash
+
+
+class UnquoteError(Exception):
+ """Internal exception used to signal failures on quoting."""
+
+
+class SecureCookie(ModificationTrackingDict):
+ """Represents a secure cookie. You can subclass this class and provide
+ an alternative mac method. The import thing is that the mac method
+ is a function with a similar interface to the hashlib. Required
+ methods are update() and digest().
+
+ Example usage:
+
+ >>> x = SecureCookie({"foo": 42, "baz": (1, 2, 3)}, "deadbeef")
+ >>> x["foo"]
+ 42
+ >>> x["baz"]
+ (1, 2, 3)
+ >>> x["blafasel"] = 23
+ >>> x.should_save
+ True
+
+ :param data: the initial data. Either a dict, list of tuples or `None`.
+ :param secret_key: the secret key. If not set `None` or not specified
+ it has to be set before :meth:`serialize` is called.
+ :param new: The initial value of the `new` flag.
+ """
+
+ #: The hash method to use. This has to be a module with a new function
+ #: or a function that creates a hashlib object. Such as `hashlib.md5`
+ #: Subclasses can override this attribute. The default hash is sha1.
+ #: Make sure to wrap this in staticmethod() if you store an arbitrary
+ #: function there such as hashlib.sha1 which might be implemented
+ #: as a function.
+ hash_method = staticmethod(_default_hash)
+
+ #: the module used for serialization. Unless overriden by subclasses
+ #: the standard pickle module is used.
+ serialization_method = pickle
+
+ #: if the contents should be base64 quoted. This can be disabled if the
+ #: serialization process returns cookie safe strings only.
+ quote_base64 = True
+
+ def __init__(self, data=None, secret_key=None, new=True):
+ ModificationTrackingDict.__init__(self, data or ())
+ # explicitly convert it into a bytestring because python 2.6
+ # no longer performs an implicit string conversion on hmac
+ if secret_key is not None:
+ secret_key = str(secret_key)
+ self.secret_key = secret_key
+ self.new = new
+
+ def __repr__(self):
+ return '<%s %s%s>' % (
+ self.__class__.__name__,
+ dict.__repr__(self),
+ self.should_save and '*' or ''
+ )
+
+ @property
+ def should_save(self):
+ """True if the session should be saved. By default this is only true
+ for :attr:`modified` cookies, not :attr:`new`.
+ """
+ return self.modified
+
+ @classmethod
+ def quote(cls, value):
+ """Quote the value for the cookie. This can be any object supported
+ by :attr:`serialization_method`.
+
+ :param value: the value to quote.
+ """
+ if cls.serialization_method is not None:
+ value = cls.serialization_method.dumps(value)
+ if cls.quote_base64:
+ value = ''.join(value.encode('base64').splitlines()).strip()
+ return value
+
+ @classmethod
+ def unquote(cls, value):
+ """Unquote the value for the cookie. If unquoting does not work a
+ :exc:`UnquoteError` is raised.
+
+ :param value: the value to unquote.
+ """
+ try:
+ if cls.quote_base64:
+ value = value.decode('base64')
+ if cls.serialization_method is not None:
+ value = cls.serialization_method.loads(value)
+ return value
+ except Exception:
+ # unfortunately pickle and other serialization modules can
+ # cause pretty every error here. if we get one we catch it
+ # and convert it into an UnquoteError
+ raise UnquoteError()
+
+ def serialize(self, expires=None):
+ """Serialize the secure cookie into a string.
+
+ If expires is provided, the session will be automatically invalidated
+ after expiration when you unseralize it. This provides better
+ protection against session cookie theft.
+
+ :param expires: an optional expiration date for the cookie (a
+ :class:`datetime.datetime` object)
+ """
+ if self.secret_key is None:
+ raise RuntimeError('no secret key defined')
+ if expires:
+ self['_expires'] = _date_to_unix(expires)
+ result = []
+ mac = hmac(self.secret_key, None, self.hash_method)
+ for key, value in sorted(self.items()):
+ result.append('%s=%s' % (
+ url_quote_plus(key),
+ self.quote(value)
+ ))
+ mac.update('|' + result[-1])
+ return '%s?%s' % (
+ mac.digest().encode('base64').strip(),
+ '&'.join(result)
+ )
+
+ @classmethod
+ def unserialize(cls, string, secret_key):
+ """Load the secure cookie from a serialized string.
+
+ :param string: the cookie value to unserialize.
+ :param secret_key: the secret key used to serialize the cookie.
+ :return: a new :class:`SecureCookie`.
+ """
+ if isinstance(string, unicode):
+ string = string.encode('utf-8', 'replace')
+ try:
+ base64_hash, data = string.split('?', 1)
+ except (ValueError, IndexError):
+ items = ()
+ else:
+ items = {}
+ mac = hmac(secret_key, None, cls.hash_method)
+ for item in data.split('&'):
+ mac.update('|' + item)
+ if not '=' in item:
+ items = None
+ break
+ key, value = item.split('=', 1)
+ # try to make the key a string
+ key = url_unquote_plus(key)
+ try:
+ key = str(key)
+ except UnicodeError:
+ pass
+ items[key] = value
+
+ # no parsing error and the mac looks okay, we can now
+ # sercurely unpickle our cookie.
+ try:
+ client_hash = base64_hash.decode('base64')
+ except Exception:
+ items = client_hash = None
+ if items is not None and safe_str_cmp(client_hash, mac.digest()):
+ try:
+ for key, value in items.iteritems():
+ items[key] = cls.unquote(value)
+ except UnquoteError:
+ items = ()
+ else:
+ if '_expires' in items:
+ if time() > items['_expires']:
+ items = ()
+ else:
+ del items['_expires']
+ else:
+ items = ()
+ return cls(items, secret_key, False)
+
+ @classmethod
+ def load_cookie(cls, request, key='session', secret_key=None):
+ """Loads a :class:`SecureCookie` from a cookie in request. If the
+ cookie is not set, a new :class:`SecureCookie` instanced is
+ returned.
+
+ :param request: a request object that has a `cookies` attribute
+ which is a dict of all cookie values.
+ :param key: the name of the cookie.
+ :param secret_key: the secret key used to unquote the cookie.
+ Always provide the value even though it has
+ no default!
+ """
+ data = request.cookies.get(key)
+ if not data:
+ return cls(secret_key=secret_key)
+ return cls.unserialize(data, secret_key)
+
+ def save_cookie(self, response, key='session', expires=None,
+ session_expires=None, max_age=None, path='/', domain=None,
+ secure=None, httponly=False, force=False):
+ """Saves the SecureCookie in a cookie on response object. All
+ parameters that are not described here are forwarded directly
+ to :meth:`~BaseResponse.set_cookie`.
+
+ :param response: a response object that has a
+ :meth:`~BaseResponse.set_cookie` method.
+ :param key: the name of the cookie.
+ :param session_expires: the expiration date of the secure cookie
+ stored information. If this is not provided
+ the cookie `expires` date is used instead.
+ """
+ if force or self.should_save:
+ data = self.serialize(session_expires or expires)
+ response.set_cookie(key, data, expires=expires, max_age=max_age,
+ path=path, domain=domain, secure=secure,
+ httponly=httponly)
diff --git a/websdk/werkzeug/contrib/sessions.py b/websdk/werkzeug/contrib/sessions.py
new file mode 100644
index 0000000..b81351a
--- /dev/null
+++ b/websdk/werkzeug/contrib/sessions.py
@@ -0,0 +1,344 @@
+# -*- coding: utf-8 -*-
+r"""
+ werkzeug.contrib.sessions
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ This module contains some helper classes that help one to add session
+ support to a python WSGI application. For full client-side session
+ storage see :mod:`~werkzeug.contrib.securecookie` which implements a
+ secure, client-side session storage.
+
+
+ Application Integration
+ =======================
+
+ ::
+
+ from werkzeug.contrib.sessions import SessionMiddleware, \
+ FilesystemSessionStore
+
+ app = SessionMiddleware(app, FilesystemSessionStore())
+
+ The current session will then appear in the WSGI environment as
+ `werkzeug.session`. However it's recommended to not use the middleware
+ but the stores directly in the application. However for very simple
+ scripts a middleware for sessions could be sufficient.
+
+ This module does not implement methods or ways to check if a session is
+ expired. That should be done by a cronjob and storage specific. For
+ example to prune unused filesystem sessions one could check the modified
+ time of the files. It sessions are stored in the database the new()
+ method should add an expiration timestamp for the session.
+
+ For better flexibility it's recommended to not use the middleware but the
+ store and session object directly in the application dispatching::
+
+ session_store = FilesystemSessionStore()
+
+ def application(environ, start_response):
+ request = Request(environ)
+ sid = request.cookies.get('cookie_name')
+ if sid is None:
+ request.session = session_store.new()
+ else:
+ request.session = session_store.get(sid)
+ response = get_the_response_object(request)
+ if request.session.should_save:
+ session_store.save(request.session)
+ response.set_cookie('cookie_name', request.session.sid)
+ return response(environ, start_response)
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+import os
+import sys
+import tempfile
+from os import path
+from time import time
+from random import random
+try:
+ from hashlib import sha1
+except ImportError:
+ from sha import new as sha1
+from cPickle import dump, load, HIGHEST_PROTOCOL
+
+from werkzeug.datastructures import CallbackDict
+from werkzeug.utils import dump_cookie, parse_cookie
+from werkzeug.wsgi import ClosingIterator
+from werkzeug.posixemulation import rename
+
+
+_sha1_re = re.compile(r'^[a-f0-9]{40}$')
+
+
+def _urandom():
+ if hasattr(os, 'urandom'):
+ return os.urandom(30)
+ return random()
+
+
+def generate_key(salt=None):
+ return sha1('%s%s%s' % (salt, time(), _urandom())).hexdigest()
+
+
+class ModificationTrackingDict(CallbackDict):
+ __slots__ = ('modified',)
+
+ def __init__(self, *args, **kwargs):
+ def on_update(self):
+ self.modified = True
+ self.modified = False
+ CallbackDict.__init__(self, on_update=on_update)
+ dict.update(self, *args, **kwargs)
+
+ def copy(self):
+ """Create a flat copy of the dict."""
+ missing = object()
+ result = object.__new__(self.__class__)
+ for name in self.__slots__:
+ val = getattr(self, name, missing)
+ if val is not missing:
+ setattr(result, name, val)
+ return result
+
+ def __copy__(self):
+ return self.copy()
+
+
+class Session(ModificationTrackingDict):
+ """Subclass of a dict that keeps track of direct object changes. Changes
+ in mutable structures are not tracked, for those you have to set
+ `modified` to `True` by hand.
+ """
+ __slots__ = ModificationTrackingDict.__slots__ + ('sid', 'new')
+
+ def __init__(self, data, sid, new=False):
+ ModificationTrackingDict.__init__(self, data)
+ self.sid = sid
+ self.new = new
+
+ def __repr__(self):
+ return '<%s %s%s>' % (
+ self.__class__.__name__,
+ dict.__repr__(self),
+ self.should_save and '*' or ''
+ )
+
+ @property
+ def should_save(self):
+ """True if the session should be saved.
+
+ .. versionchanged:: 0.6
+ By default the session is now only saved if the session is
+ modified, not if it is new like it was before.
+ """
+ return self.modified
+
+
+class SessionStore(object):
+ """Baseclass for all session stores. The Werkzeug contrib module does not
+ implement any useful stores besides the filesystem store, application
+ developers are encouraged to create their own stores.
+
+ :param session_class: The session class to use. Defaults to
+ :class:`Session`.
+ """
+
+ def __init__(self, session_class=None):
+ if session_class is None:
+ session_class = Session
+ self.session_class = session_class
+
+ def is_valid_key(self, key):
+ """Check if a key has the correct format."""
+ return _sha1_re.match(key) is not None
+
+ def generate_key(self, salt=None):
+ """Simple function that generates a new session key."""
+ return generate_key(salt)
+
+ def new(self):
+ """Generate a new session."""
+ return self.session_class({}, self.generate_key(), True)
+
+ def save(self, session):
+ """Save a session."""
+
+ def save_if_modified(self, session):
+ """Save if a session class wants an update."""
+ if session.should_save:
+ self.save(session)
+
+ def delete(self, session):
+ """Delete a session."""
+
+ def get(self, sid):
+ """Get a session for this sid or a new session object. This method
+ has to check if the session key is valid and create a new session if
+ that wasn't the case.
+ """
+ return self.session_class({}, sid, True)
+
+
+#: used for temporary files by the filesystem session store
+_fs_transaction_suffix = '.__wz_sess'
+
+
+class FilesystemSessionStore(SessionStore):
+ """Simple example session store that saves sessions on the filesystem.
+ This store works best on POSIX systems and Windows Vista / Windows
+ Server 2008 and newer.
+
+ .. versionchanged:: 0.6
+ `renew_missing` was added. Previously this was considered `True`,
+ now the default changed to `False` and it can be explicitly
+ deactivated.
+
+ :param path: the path to the folder used for storing the sessions.
+ If not provided the default temporary directory is used.
+ :param filename_template: a string template used to give the session
+ a filename. ``%s`` is replaced with the
+ session id.
+ :param session_class: The session class to use. Defaults to
+ :class:`Session`.
+ :param renew_missing: set to `True` if you want the store to
+ give the user a new sid if the session was
+ not yet saved.
+ """
+
+ def __init__(self, path=None, filename_template='werkzeug_%s.sess',
+ session_class=None, renew_missing=False, mode=0644):
+ SessionStore.__init__(self, session_class)
+ if path is None:
+ path = tempfile.gettempdir()
+ self.path = path
+ if isinstance(filename_template, unicode):
+ filename_template = filename_template.encode(
+ sys.getfilesystemencoding() or 'utf-8')
+ assert not filename_template.endswith(_fs_transaction_suffix), \
+ 'filename templates may not end with %s' % _fs_transaction_suffix
+ self.filename_template = filename_template
+ self.renew_missing = renew_missing
+ self.mode = mode
+
+ def get_session_filename(self, sid):
+ # out of the box, this should be a strict ASCII subset but
+ # you might reconfigure the session object to have a more
+ # arbitrary string.
+ if isinstance(sid, unicode):
+ sid = sid.encode(sys.getfilesystemencoding() or 'utf-8')
+ return path.join(self.path, self.filename_template % sid)
+
+ def save(self, session):
+ fn = self.get_session_filename(session.sid)
+ fd, tmp = tempfile.mkstemp(suffix=_fs_transaction_suffix,
+ dir=self.path)
+ f = os.fdopen(fd, 'wb')
+ try:
+ dump(dict(session), f, HIGHEST_PROTOCOL)
+ finally:
+ f.close()
+ try:
+ rename(tmp, fn)
+ os.chmod(fn, self.mode)
+ except (IOError, OSError):
+ pass
+
+ def delete(self, session):
+ fn = self.get_session_filename(session.sid)
+ try:
+ os.unlink(fn)
+ except OSError:
+ pass
+
+ def get(self, sid):
+ if not self.is_valid_key(sid):
+ return self.new()
+ try:
+ f = open(self.get_session_filename(sid), 'rb')
+ except IOError:
+ if self.renew_missing:
+ return self.new()
+ data = {}
+ else:
+ try:
+ try:
+ data = load(f)
+ except Exception:
+ data = {}
+ finally:
+ f.close()
+ return self.session_class(data, sid, False)
+
+ def list(self):
+ """Lists all sessions in the store.
+
+ .. versionadded:: 0.6
+ """
+ before, after = self.filename_template.split('%s', 1)
+ filename_re = re.compile(r'%s(.{5,})%s$' % (re.escape(before),
+ re.escape(after)))
+ result = []
+ for filename in os.listdir(self.path):
+ #: this is a session that is still being saved.
+ if filename.endswith(_fs_transaction_suffix):
+ continue
+ match = filename_re.match(filename)
+ if match is not None:
+ result.append(match.group(1))
+ return result
+
+
+class SessionMiddleware(object):
+ """A simple middleware that puts the session object of a store provided
+ into the WSGI environ. It automatically sets cookies and restores
+ sessions.
+
+ However a middleware is not the preferred solution because it won't be as
+ fast as sessions managed by the application itself and will put a key into
+ the WSGI environment only relevant for the application which is against
+ the concept of WSGI.
+
+ The cookie parameters are the same as for the :func:`~dump_cookie`
+ function just prefixed with ``cookie_``. Additionally `max_age` is
+ called `cookie_age` and not `cookie_max_age` because of backwards
+ compatibility.
+ """
+
+ def __init__(self, app, store, cookie_name='session_id',
+ cookie_age=None, cookie_expires=None, cookie_path='/',
+ cookie_domain=None, cookie_secure=None,
+ cookie_httponly=False, environ_key='werkzeug.session'):
+ self.app = app
+ self.store = store
+ self.cookie_name = cookie_name
+ self.cookie_age = cookie_age
+ self.cookie_expires = cookie_expires
+ self.cookie_path = cookie_path
+ self.cookie_domain = cookie_domain
+ self.cookie_secure = cookie_secure
+ self.cookie_httponly = cookie_httponly
+ self.environ_key = environ_key
+
+ def __call__(self, environ, start_response):
+ cookie = parse_cookie(environ.get('HTTP_COOKIE', ''))
+ sid = cookie.get(self.cookie_name, None)
+ if sid is None:
+ session = self.store.new()
+ else:
+ session = self.store.get(sid)
+ environ[self.environ_key] = session
+
+ def injecting_start_response(status, headers, exc_info=None):
+ if session.should_save:
+ self.store.save(session)
+ headers.append(('Set-Cookie', dump_cookie(self.cookie_name,
+ session.sid, self.cookie_age,
+ self.cookie_expires, self.cookie_path,
+ self.cookie_domain, self.cookie_secure,
+ self.cookie_httponly)))
+ return start_response(status, headers, exc_info)
+ return ClosingIterator(self.app(environ, injecting_start_response),
+ lambda: self.store.save_if_modified(session))
diff --git a/websdk/werkzeug/contrib/testtools.py b/websdk/werkzeug/contrib/testtools.py
new file mode 100644
index 0000000..9bbf76a
--- /dev/null
+++ b/websdk/werkzeug/contrib/testtools.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib.testtools
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ This module implements extended wrappers for simplified testing.
+
+ `TestResponse`
+ A response wrapper which adds various cached attributes for
+ simplified assertions on various content types.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+from werkzeug.utils import cached_property, import_string
+from werkzeug.wrappers import Response
+
+from warnings import warn
+warn(DeprecationWarning('werkzeug.contrib.testtools is deprecated and '
+ 'will be removed with Werkzeug 1.0'))
+
+
+class ContentAccessors(object):
+ """
+ A mixin class for response objects that provides a couple of useful
+ accessors for unittesting.
+ """
+
+ def xml(self):
+ """Get an etree if possible."""
+ if 'xml' not in self.mimetype:
+ raise AttributeError(
+ 'Not a XML response (Content-Type: %s)'
+ % self.mimetype)
+ for module in ['xml.etree.ElementTree', 'ElementTree',
+ 'elementtree.ElementTree']:
+ etree = import_string(module, silent=True)
+ if etree is not None:
+ return etree.XML(self.body)
+ raise RuntimeError('You must have ElementTree installed '
+ 'to use TestResponse.xml')
+ xml = cached_property(xml)
+
+ def lxml(self):
+ """Get an lxml etree if possible."""
+ if ('html' not in self.mimetype and 'xml' not in self.mimetype):
+ raise AttributeError('Not an HTML/XML response')
+ from lxml import etree
+ try:
+ from lxml.html import fromstring
+ except ImportError:
+ fromstring = etree.HTML
+ if self.mimetype=='text/html':
+ return fromstring(self.data)
+ return etree.XML(self.data)
+ lxml = cached_property(lxml)
+
+ def json(self):
+ """Get the result of simplejson.loads if possible."""
+ if 'json' not in self.mimetype:
+ raise AttributeError('Not a JSON response')
+ try:
+ from simplejson import loads
+ except ImportError:
+ from json import loads
+ return loads(self.data)
+ json = cached_property(json)
+
+
+class TestResponse(Response, ContentAccessors):
+ """Pass this to `werkzeug.test.Client` for easier unittesting."""
diff --git a/websdk/werkzeug/contrib/wrappers.py b/websdk/werkzeug/contrib/wrappers.py
new file mode 100644
index 0000000..bd6a2d4
--- /dev/null
+++ b/websdk/werkzeug/contrib/wrappers.py
@@ -0,0 +1,275 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.contrib.wrappers
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Extra wrappers or mixins contributed by the community. These wrappers can
+ be mixed in into request objects to add extra functionality.
+
+ Example::
+
+ from werkzeug.wrappers import Request as RequestBase
+ from werkzeug.contrib.wrappers import JSONRequestMixin
+
+ class Request(RequestBase, JSONRequestMixin):
+ pass
+
+ Afterwards this request object provides the extra functionality of the
+ :class:`JSONRequestMixin`.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import codecs
+from werkzeug.exceptions import BadRequest
+from werkzeug.utils import cached_property
+from werkzeug.http import dump_options_header, parse_options_header
+from werkzeug._internal import _decode_unicode
+try:
+ from simplejson import loads
+except ImportError:
+ from json import loads
+
+
+def is_known_charset(charset):
+ """Checks if the given charset is known to Python."""
+ try:
+ codecs.lookup(charset)
+ except LookupError:
+ return False
+ return True
+
+
+class JSONRequestMixin(object):
+ """Add json method to a request object. This will parse the input data
+ through simplejson if possible.
+
+ :exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type
+ is not json or if the data itself cannot be parsed as json.
+ """
+
+ @cached_property
+ def json(self):
+ """Get the result of simplejson.loads if possible."""
+ if 'json' not in self.environ.get('CONTENT_TYPE', ''):
+ raise BadRequest('Not a JSON request')
+ try:
+ return loads(self.data)
+ except Exception:
+ raise BadRequest('Unable to read JSON request')
+
+
+class ProtobufRequestMixin(object):
+ """Add protobuf parsing method to a request object. This will parse the
+ input data through `protobuf`_ if possible.
+
+ :exc:`~werkzeug.exceptions.BadRequest` will be raised if the content-type
+ is not protobuf or if the data itself cannot be parsed property.
+
+ .. _protobuf: http://code.google.com/p/protobuf/
+ """
+
+ #: by default the :class:`ProtobufRequestMixin` will raise a
+ #: :exc:`~werkzeug.exceptions.BadRequest` if the object is not
+ #: initialized. You can bypass that check by setting this
+ #: attribute to `False`.
+ protobuf_check_initialization = True
+
+ def parse_protobuf(self, proto_type):
+ """Parse the data into an instance of proto_type."""
+ if 'protobuf' not in self.environ.get('CONTENT_TYPE', ''):
+ raise BadRequest('Not a Protobuf request')
+
+ obj = proto_type()
+ try:
+ obj.ParseFromString(self.data)
+ except Exception:
+ raise BadRequest("Unable to parse Protobuf request")
+
+ # Fail if not all required fields are set
+ if self.protobuf_check_initialization and not obj.IsInitialized():
+ raise BadRequest("Partial Protobuf request")
+
+ return obj
+
+
+class RoutingArgsRequestMixin(object):
+ """This request mixin adds support for the wsgiorg routing args
+ `specification`_.
+
+ .. _specification: http://www.wsgi.org/wsgi/Specifications/routing_args
+ """
+
+ def _get_routing_args(self):
+ return self.environ.get('wsgiorg.routing_args', (()))[0]
+
+ def _set_routing_args(self, value):
+ if self.shallow:
+ raise RuntimeError('A shallow request tried to modify the WSGI '
+ 'environment. If you really want to do that, '
+ 'set `shallow` to False.')
+ self.environ['wsgiorg.routing_args'] = (value, self.routing_vars)
+
+ routing_args = property(_get_routing_args, _set_routing_args, doc='''
+ The positional URL arguments as `tuple`.''')
+ del _get_routing_args, _set_routing_args
+
+ def _get_routing_vars(self):
+ rv = self.environ.get('wsgiorg.routing_args')
+ if rv is not None:
+ return rv[1]
+ rv = {}
+ if not self.shallow:
+ self.routing_vars = rv
+ return rv
+
+ def _set_routing_vars(self, value):
+ if self.shallow:
+ raise RuntimeError('A shallow request tried to modify the WSGI '
+ 'environment. If you really want to do that, '
+ 'set `shallow` to False.')
+ self.environ['wsgiorg.routing_args'] = (self.routing_args, value)
+
+ routing_vars = property(_get_routing_vars, _set_routing_vars, doc='''
+ The keyword URL arguments as `dict`.''')
+ del _get_routing_vars, _set_routing_vars
+
+
+class ReverseSlashBehaviorRequestMixin(object):
+ """This mixin reverses the trailing slash behavior of :attr:`script_root`
+ and :attr:`path`. This makes it possible to use :func:`~urlparse.urljoin`
+ directly on the paths.
+
+ Because it changes the behavior or :class:`Request` this class has to be
+ mixed in *before* the actual request class::
+
+ class MyRequest(ReverseSlashBehaviorRequestMixin, Request):
+ pass
+
+ This example shows the differences (for an application mounted on
+ `/application` and the request going to `/application/foo/bar`):
+
+ +---------------+-------------------+---------------------+
+ | | normal behavior | reverse behavior |
+ +===============+===================+=====================+
+ | `script_root` | ``/application`` | ``/application/`` |
+ +---------------+-------------------+---------------------+
+ | `path` | ``/foo/bar`` | ``foo/bar`` |
+ +---------------+-------------------+---------------------+
+ """
+
+ @cached_property
+ def path(self):
+ """Requested path as unicode. This works a bit like the regular path
+ info in the WSGI environment but will not include a leading slash.
+ """
+ path = (self.environ.get('PATH_INFO') or '').lstrip('/')
+ return _decode_unicode(path, self.charset, self.encoding_errors)
+
+ @cached_property
+ def script_root(self):
+ """The root path of the script includling a trailing slash."""
+ path = (self.environ.get('SCRIPT_NAME') or '').rstrip('/') + '/'
+ return _decode_unicode(path, self.charset, self.encoding_errors)
+
+
+class DynamicCharsetRequestMixin(object):
+ """"If this mixin is mixed into a request class it will provide
+ a dynamic `charset` attribute. This means that if the charset is
+ transmitted in the content type headers it's used from there.
+
+ Because it changes the behavior or :class:`Request` this class has
+ to be mixed in *before* the actual request class::
+
+ class MyRequest(DynamicCharsetRequestMixin, Request):
+ pass
+
+ By default the request object assumes that the URL charset is the
+ same as the data charset. If the charset varies on each request
+ based on the transmitted data it's not a good idea to let the URLs
+ change based on that. Most browsers assume either utf-8 or latin1
+ for the URLs if they have troubles figuring out. It's strongly
+ recommended to set the URL charset to utf-8::
+
+ class MyRequest(DynamicCharsetRequestMixin, Request):
+ url_charset = 'utf-8'
+
+ .. versionadded:: 0.6
+ """
+
+ #: the default charset that is assumed if the content type header
+ #: is missing or does not contain a charset parameter. The default
+ #: is latin1 which is what HTTP specifies as default charset.
+ #: You may however want to set this to utf-8 to better support
+ #: browsers that do not transmit a charset for incoming data.
+ default_charset = 'latin1'
+
+ def unknown_charset(self, charset):
+ """Called if a charset was provided but is not supported by
+ the Python codecs module. By default latin1 is assumed then
+ to not lose any information, you may override this method to
+ change the behavior.
+
+ :param charset: the charset that was not found.
+ :return: the replacement charset.
+ """
+ return 'latin1'
+
+ @cached_property
+ def charset(self):
+ """The charset from the content type."""
+ header = self.environ.get('CONTENT_TYPE')
+ if header:
+ ct, options = parse_options_header(header)
+ charset = options.get('charset')
+ if charset:
+ if is_known_charset(charset):
+ return charset
+ return self.unknown_charset(charset)
+ return self.default_charset
+
+
+class DynamicCharsetResponseMixin(object):
+ """If this mixin is mixed into a response class it will provide
+ a dynamic `charset` attribute. This means that if the charset is
+ looked up and stored in the `Content-Type` header and updates
+ itself automatically. This also means a small performance hit but
+ can be useful if you're working with different charsets on
+ responses.
+
+ Because the charset attribute is no a property at class-level, the
+ default value is stored in `default_charset`.
+
+ Because it changes the behavior or :class:`Response` this class has
+ to be mixed in *before* the actual response class::
+
+ class MyResponse(DynamicCharsetResponseMixin, Response):
+ pass
+
+ .. versionadded:: 0.6
+ """
+
+ #: the default charset.
+ default_charset = 'utf-8'
+
+ def _get_charset(self):
+ header = self.headers.get('content-type')
+ if header:
+ charset = parse_options_header(header)[1].get('charset')
+ if charset:
+ return charset
+ return self.default_charset
+
+ def _set_charset(self, charset):
+ header = self.headers.get('content-type')
+ ct, options = parse_options_header(header)
+ if not ct:
+ raise TypeError('Cannot set charset if Content-Type '
+ 'header is missing.')
+ options['charset'] = charset
+ self.headers['Content-Type'] = dump_options_header(ct, options)
+
+ charset = property(_get_charset, _set_charset, doc="""
+ The charset for the response. It's stored inside the
+ Content-Type header as a parameter.""")
+ del _get_charset, _set_charset
diff --git a/websdk/werkzeug/datastructures.py b/websdk/werkzeug/datastructures.py
new file mode 100644
index 0000000..8f8498c
--- /dev/null
+++ b/websdk/werkzeug/datastructures.py
@@ -0,0 +1,2567 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.datastructures
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ This module provides mixins and classes with an immutable interface.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+import codecs
+import mimetypes
+from itertools import repeat
+
+from werkzeug._internal import _proxy_repr, _missing, _empty_stream
+
+
+_locale_delim_re = re.compile(r'[_-]')
+
+
+def is_immutable(self):
+ raise TypeError('%r objects are immutable' % self.__class__.__name__)
+
+
+def iter_multi_items(mapping):
+ """Iterates over the items of a mapping yielding keys and values
+ without dropping any from more complex structures.
+ """
+ if isinstance(mapping, MultiDict):
+ for item in mapping.iteritems(multi=True):
+ yield item
+ elif isinstance(mapping, dict):
+ for key, value in mapping.iteritems():
+ if isinstance(value, (tuple, list)):
+ for value in value:
+ yield key, value
+ else:
+ yield key, value
+ else:
+ for item in mapping:
+ yield item
+
+
+class ImmutableListMixin(object):
+ """Makes a :class:`list` immutable.
+
+ .. versionadded:: 0.5
+
+ :private:
+ """
+
+ _hash_cache = None
+
+ def __hash__(self):
+ if self._hash_cache is not None:
+ return self._hash_cache
+ rv = self._hash_cache = hash(tuple(self))
+ return rv
+
+ def __reduce_ex__(self, protocol):
+ return type(self), (list(self),)
+
+ def __delitem__(self, key):
+ is_immutable(self)
+
+ def __delslice__(self, i, j):
+ is_immutable(self)
+
+ def __iadd__(self, other):
+ is_immutable(self)
+ __imul__ = __iadd__
+
+ def __setitem__(self, key, value):
+ is_immutable(self)
+
+ def __setslice__(self, i, j, value):
+ is_immutable(self)
+
+ def append(self, item):
+ is_immutable(self)
+ remove = append
+
+ def extend(self, iterable):
+ is_immutable(self)
+
+ def insert(self, pos, value):
+ is_immutable(self)
+
+ def pop(self, index=-1):
+ is_immutable(self)
+
+ def reverse(self):
+ is_immutable(self)
+
+ def sort(self, cmp=None, key=None, reverse=None):
+ is_immutable(self)
+
+
+class ImmutableList(ImmutableListMixin, list):
+ """An immutable :class:`list`.
+
+ .. versionadded:: 0.5
+
+ :private:
+ """
+
+ __repr__ = _proxy_repr(list)
+
+
+class ImmutableDictMixin(object):
+ """Makes a :class:`dict` immutable.
+
+ .. versionadded:: 0.5
+
+ :private:
+ """
+ _hash_cache = None
+
+ @classmethod
+ def fromkeys(cls, keys, value=None):
+ instance = super(cls, cls).__new__(cls)
+ instance.__init__(zip(keys, repeat(value)))
+ return instance
+
+ def __reduce_ex__(self, protocol):
+ return type(self), (dict(self),)
+
+ def _iter_hashitems(self):
+ return self.iteritems()
+
+ def __hash__(self):
+ if self._hash_cache is not None:
+ return self._hash_cache
+ rv = self._hash_cache = hash(frozenset(self._iter_hashitems()))
+ return rv
+
+ def setdefault(self, key, default=None):
+ is_immutable(self)
+
+ def update(self, *args, **kwargs):
+ is_immutable(self)
+
+ def pop(self, key, default=None):
+ is_immutable(self)
+
+ def popitem(self):
+ is_immutable(self)
+
+ def __setitem__(self, key, value):
+ is_immutable(self)
+
+ def __delitem__(self, key):
+ is_immutable(self)
+
+ def clear(self):
+ is_immutable(self)
+
+
+class ImmutableMultiDictMixin(ImmutableDictMixin):
+ """Makes a :class:`MultiDict` immutable.
+
+ .. versionadded:: 0.5
+
+ :private:
+ """
+
+ def __reduce_ex__(self, protocol):
+ return type(self), (self.items(multi=True),)
+
+ def _iter_hashitems(self):
+ return enumerate(self.iteritems(multi=True))
+
+ def add(self, key, value):
+ is_immutable(self)
+
+ def popitemlist(self):
+ is_immutable(self)
+
+ def poplist(self, key):
+ is_immutable(self)
+
+ def setlist(self, key, new_list):
+ is_immutable(self)
+
+ def setlistdefault(self, key, default_list=None):
+ is_immutable(self)
+
+
+class UpdateDictMixin(object):
+ """Makes dicts call `self.on_update` on modifications.
+
+ .. versionadded:: 0.5
+
+ :private:
+ """
+
+ on_update = None
+
+ def calls_update(name):
+ def oncall(self, *args, **kw):
+ rv = getattr(super(UpdateDictMixin, self), name)(*args, **kw)
+ if self.on_update is not None:
+ self.on_update(self)
+ return rv
+ oncall.__name__ = name
+ return oncall
+
+ __setitem__ = calls_update('__setitem__')
+ __delitem__ = calls_update('__delitem__')
+ clear = calls_update('clear')
+ pop = calls_update('pop')
+ popitem = calls_update('popitem')
+ setdefault = calls_update('setdefault')
+ update = calls_update('update')
+ del calls_update
+
+
+class TypeConversionDict(dict):
+ """Works like a regular dict but the :meth:`get` method can perform
+ type conversions. :class:`MultiDict` and :class:`CombinedMultiDict`
+ are subclasses of this class and provide the same feature.
+
+ .. versionadded:: 0.5
+ """
+
+ def get(self, key, default=None, type=None):
+ """Return the default value if the requested data doesn't exist.
+ If `type` is provided and is a callable it should convert the value,
+ return it or raise a :exc:`ValueError` if that is not possible. In
+ this case the function will return the default as if the value was not
+ found:
+
+ >>> d = TypeConversionDict(foo='42', bar='blub')
+ >>> d.get('foo', type=int)
+ 42
+ >>> d.get('bar', -1, type=int)
+ -1
+
+ :param key: The key to be looked up.
+ :param default: The default value to be returned if the key can't
+ be looked up. If not further specified `None` is
+ returned.
+ :param type: A callable that is used to cast the value in the
+ :class:`MultiDict`. If a :exc:`ValueError` is raised
+ by this callable the default value is returned.
+ """
+ try:
+ rv = self[key]
+ if type is not None:
+ rv = type(rv)
+ except (KeyError, ValueError):
+ rv = default
+ return rv
+
+
+class ImmutableTypeConversionDict(ImmutableDictMixin, TypeConversionDict):
+ """Works like a :class:`TypeConversionDict` but does not support
+ modifications.
+
+ .. versionadded:: 0.5
+ """
+
+ def copy(self):
+ """Return a shallow mutable copy of this object. Keep in mind that
+ the standard library's :func:`copy` function is a no-op for this class
+ like for any other python immutable type (eg: :class:`tuple`).
+ """
+ return TypeConversionDict(self)
+
+ def __copy__(self):
+ return self
+
+
+class MultiDict(TypeConversionDict):
+ """A :class:`MultiDict` is a dictionary subclass customized to deal with
+ multiple values for the same key which is for example used by the parsing
+ functions in the wrappers. This is necessary because some HTML form
+ elements pass multiple values for the same key.
+
+ :class:`MultiDict` implements all standard dictionary methods.
+ Internally, it saves all values for a key as a list, but the standard dict
+ access methods will only return the first value for a key. If you want to
+ gain access to the other values, too, you have to use the `list` methods as
+ explained below.
+
+ Basic Usage:
+
+ >>> d = MultiDict([('a', 'b'), ('a', 'c')])
+ >>> d
+ MultiDict([('a', 'b'), ('a', 'c')])
+ >>> d['a']
+ 'b'
+ >>> d.getlist('a')
+ ['b', 'c']
+ >>> 'a' in d
+ True
+
+ It behaves like a normal dict thus all dict functions will only return the
+ first value when multiple values for one key are found.
+
+ From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
+ subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
+ render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
+ exceptions.
+
+ A :class:`MultiDict` can be constructed from an iterable of
+ ``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2
+ onwards some keyword parameters.
+
+ :param mapping: the initial value for the :class:`MultiDict`. Either a
+ regular dict, an iterable of ``(key, value)`` tuples
+ or `None`.
+ """
+
+ def __init__(self, mapping=None):
+ if isinstance(mapping, MultiDict):
+ dict.__init__(self, ((k, l[:]) for k, l in mapping.iterlists()))
+ elif isinstance(mapping, dict):
+ tmp = {}
+ for key, value in mapping.iteritems():
+ if isinstance(value, (tuple, list)):
+ value = list(value)
+ else:
+ value = [value]
+ tmp[key] = value
+ dict.__init__(self, tmp)
+ else:
+ tmp = {}
+ for key, value in mapping or ():
+ tmp.setdefault(key, []).append(value)
+ dict.__init__(self, tmp)
+
+ def __getstate__(self):
+ return dict(self.lists())
+
+ def __setstate__(self, value):
+ dict.clear(self)
+ dict.update(self, value)
+
+ def __iter__(self):
+ return self.iterkeys()
+
+ def __getitem__(self, key):
+ """Return the first data value for this key;
+ raises KeyError if not found.
+
+ :param key: The key to be looked up.
+ :raise KeyError: if the key does not exist.
+ """
+ if key in self:
+ return dict.__getitem__(self, key)[0]
+ raise BadRequestKeyError(key)
+
+ def __setitem__(self, key, value):
+ """Like :meth:`add` but removes an existing key first.
+
+ :param key: the key for the value.
+ :param value: the value to set.
+ """
+ dict.__setitem__(self, key, [value])
+
+ def add(self, key, value):
+ """Adds a new value for the key.
+
+ .. versionadded:: 0.6
+
+ :param key: the key for the value.
+ :param value: the value to add.
+ """
+ dict.setdefault(self, key, []).append(value)
+
+ def getlist(self, key, type=None):
+ """Return the list of items for a given key. If that key is not in the
+ `MultiDict`, the return value will be an empty list. Just as `get`
+ `getlist` accepts a `type` parameter. All items will be converted
+ with the callable defined there.
+
+ :param key: The key to be looked up.
+ :param type: A callable that is used to cast the value in the
+ :class:`MultiDict`. If a :exc:`ValueError` is raised
+ by this callable the value will be removed from the list.
+ :return: a :class:`list` of all the values for the key.
+ """
+ try:
+ rv = dict.__getitem__(self, key)
+ except KeyError:
+ return []
+ if type is None:
+ return list(rv)
+ result = []
+ for item in rv:
+ try:
+ result.append(type(item))
+ except ValueError:
+ pass
+ return result
+
+ def setlist(self, key, new_list):
+ """Remove the old values for a key and add new ones. Note that the list
+ you pass the values in will be shallow-copied before it is inserted in
+ the dictionary.
+
+ >>> d = MultiDict()
+ >>> d.setlist('foo', ['1', '2'])
+ >>> d['foo']
+ '1'
+ >>> d.getlist('foo')
+ ['1', '2']
+
+ :param key: The key for which the values are set.
+ :param new_list: An iterable with the new values for the key. Old values
+ are removed first.
+ """
+ dict.__setitem__(self, key, list(new_list))
+
+ def setdefault(self, key, default=None):
+ """Returns the value for the key if it is in the dict, otherwise it
+ returns `default` and sets that value for `key`.
+
+ :param key: The key to be looked up.
+ :param default: The default value to be returned if the key is not
+ in the dict. If not further specified it's `None`.
+ """
+ if key not in self:
+ self[key] = default
+ else:
+ default = self[key]
+ return default
+
+ def setlistdefault(self, key, default_list=None):
+ """Like `setdefault` but sets multiple values. The list returned
+ is not a copy, but the list that is actually used internally. This
+ means that you can put new values into the dict by appending items
+ to the list:
+
+ >>> d = MultiDict({"foo": 1})
+ >>> d.setlistdefault("foo").extend([2, 3])
+ >>> d.getlist("foo")
+ [1, 2, 3]
+
+ :param key: The key to be looked up.
+ :param default: An iterable of default values. It is either copied
+ (in case it was a list) or converted into a list
+ before returned.
+ :return: a :class:`list`
+ """
+ if key not in self:
+ default_list = list(default_list or ())
+ dict.__setitem__(self, key, default_list)
+ else:
+ default_list = dict.__getitem__(self, key)
+ return default_list
+
+ def items(self, multi=False):
+ """Return a list of ``(key, value)`` pairs.
+
+ :param multi: If set to `True` the list returned will have a
+ pair for each value of each key. Otherwise it
+ will only contain pairs for the first value of
+ each key.
+
+ :return: a :class:`list`
+ """
+ return list(self.iteritems(multi))
+
+ def lists(self):
+ """Return a list of ``(key, values)`` pairs, where values is the list of
+ all values associated with the key.
+
+ :return: a :class:`list`
+ """
+ return list(self.iterlists())
+
+ def values(self):
+ """Returns a list of the first value on every key's value list.
+
+ :return: a :class:`list`.
+ """
+ return [self[key] for key in self.iterkeys()]
+
+ def listvalues(self):
+ """Return a list of all values associated with a key. Zipping
+ :meth:`keys` and this is the same as calling :meth:`lists`:
+
+ >>> d = MultiDict({"foo": [1, 2, 3]})
+ >>> zip(d.keys(), d.listvalues()) == d.lists()
+ True
+
+ :return: a :class:`list`
+ """
+ return list(self.iterlistvalues())
+
+ def iteritems(self, multi=False):
+ """Like :meth:`items` but returns an iterator."""
+ for key, values in dict.iteritems(self):
+ if multi:
+ for value in values:
+ yield key, value
+ else:
+ yield key, values[0]
+
+ def iterlists(self):
+ """Like :meth:`items` but returns an iterator."""
+ for key, values in dict.iteritems(self):
+ yield key, list(values)
+
+ def itervalues(self):
+ """Like :meth:`values` but returns an iterator."""
+ for values in dict.itervalues(self):
+ yield values[0]
+
+ def iterlistvalues(self):
+ """Like :meth:`listvalues` but returns an iterator."""
+ return dict.itervalues(self)
+
+ def copy(self):
+ """Return a shallow copy of this object."""
+ return self.__class__(self)
+
+ def to_dict(self, flat=True):
+ """Return the contents as regular dict. If `flat` is `True` the
+ returned dict will only have the first item present, if `flat` is
+ `False` all values will be returned as lists.
+
+ :param flat: If set to `False` the dict returned will have lists
+ with all the values in it. Otherwise it will only
+ contain the first value for each key.
+ :return: a :class:`dict`
+ """
+ if flat:
+ return dict(self.iteritems())
+ return dict(self.lists())
+
+ def update(self, other_dict):
+ """update() extends rather than replaces existing key lists."""
+ for key, value in iter_multi_items(other_dict):
+ MultiDict.add(self, key, value)
+
+ def pop(self, key, default=_missing):
+ """Pop the first item for a list on the dict. Afterwards the
+ key is removed from the dict, so additional values are discarded:
+
+ >>> d = MultiDict({"foo": [1, 2, 3]})
+ >>> d.pop("foo")
+ 1
+ >>> "foo" in d
+ False
+
+ :param key: the key to pop.
+ :param default: if provided the value to return if the key was
+ not in the dictionary.
+ """
+ try:
+ return dict.pop(self, key)[0]
+ except KeyError, e:
+ if default is not _missing:
+ return default
+ raise BadRequestKeyError(str(e))
+
+ def popitem(self):
+ """Pop an item from the dict."""
+ try:
+ item = dict.popitem(self)
+ return (item[0], item[1][0])
+ except KeyError, e:
+ raise BadRequestKeyError(str(e))
+
+ def poplist(self, key):
+ """Pop the list for a key from the dict. If the key is not in the dict
+ an empty list is returned.
+
+ .. versionchanged:: 0.5
+ If the key does no longer exist a list is returned instead of
+ raising an error.
+ """
+ return dict.pop(self, key, [])
+
+ def popitemlist(self):
+ """Pop a ``(key, list)`` tuple from the dict."""
+ try:
+ return dict.popitem(self)
+ except KeyError, e:
+ raise BadRequestKeyError(str(e))
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.items(multi=True))
+
+
+class _omd_bucket(object):
+ """Wraps values in the :class:`OrderedMultiDict`. This makes it
+ possible to keep an order over multiple different keys. It requires
+ a lot of extra memory and slows down access a lot, but makes it
+ possible to access elements in O(1) and iterate in O(n).
+ """
+ __slots__ = ('prev', 'key', 'value', 'next')
+
+ def __init__(self, omd, key, value):
+ self.prev = omd._last_bucket
+ self.key = key
+ self.value = value
+ self.next = None
+
+ if omd._first_bucket is None:
+ omd._first_bucket = self
+ if omd._last_bucket is not None:
+ omd._last_bucket.next = self
+ omd._last_bucket = self
+
+ def unlink(self, omd):
+ if self.prev:
+ self.prev.next = self.next
+ if self.next:
+ self.next.prev = self.prev
+ if omd._first_bucket is self:
+ omd._first_bucket = self.next
+ if omd._last_bucket is self:
+ omd._last_bucket = self.prev
+
+
+class OrderedMultiDict(MultiDict):
+ """Works like a regular :class:`MultiDict` but preserves the
+ order of the fields. To convert the ordered multi dict into a
+ list you can use the :meth:`items` method and pass it ``multi=True``.
+
+ In general an :class:`OrderedMultiDict` is an order of magnitude
+ slower than a :class:`MultiDict`.
+
+ .. admonition:: note
+
+ Due to a limitation in Python you cannot convert an ordered
+ multi dict into a regular dict by using ``dict(multidict)``.
+ Instead you have to use the :meth:`to_dict` method, otherwise
+ the internal bucket objects are exposed.
+ """
+
+ def __init__(self, mapping=None):
+ dict.__init__(self)
+ self._first_bucket = self._last_bucket = None
+ if mapping is not None:
+ OrderedMultiDict.update(self, mapping)
+
+ def __eq__(self, other):
+ if not isinstance(other, MultiDict):
+ return NotImplemented
+ if isinstance(other, OrderedMultiDict):
+ iter1 = self.iteritems(multi=True)
+ iter2 = other.iteritems(multi=True)
+ try:
+ for k1, v1 in iter1:
+ k2, v2 = iter2.next()
+ if k1 != k2 or v1 != v2:
+ return False
+ except StopIteration:
+ return False
+ try:
+ iter2.next()
+ except StopIteration:
+ return True
+ return False
+ if len(self) != len(other):
+ return False
+ for key, values in self.iterlists():
+ if other.getlist(key) != values:
+ return False
+ return True
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __reduce_ex__(self, protocol):
+ return type(self), (self.items(multi=True),)
+
+ def __getstate__(self):
+ return self.items(multi=True)
+
+ def __setstate__(self, values):
+ dict.clear(self)
+ for key, value in values:
+ self.add(key, value)
+
+ def __getitem__(self, key):
+ if key in self:
+ return dict.__getitem__(self, key)[0].value
+ raise BadRequestKeyError(key)
+
+ def __setitem__(self, key, value):
+ self.poplist(key)
+ self.add(key, value)
+
+ def __delitem__(self, key):
+ self.pop(key)
+
+ def iterkeys(self):
+ return (key for key, value in self.iteritems())
+
+ def itervalues(self):
+ return (value for key, value in self.iteritems())
+
+ def iteritems(self, multi=False):
+ ptr = self._first_bucket
+ if multi:
+ while ptr is not None:
+ yield ptr.key, ptr.value
+ ptr = ptr.next
+ else:
+ returned_keys = set()
+ while ptr is not None:
+ if ptr.key not in returned_keys:
+ returned_keys.add(ptr.key)
+ yield ptr.key, ptr.value
+ ptr = ptr.next
+
+ def iterlists(self):
+ returned_keys = set()
+ ptr = self._first_bucket
+ while ptr is not None:
+ if ptr.key not in returned_keys:
+ yield ptr.key, self.getlist(ptr.key)
+ returned_keys.add(ptr.key)
+ ptr = ptr.next
+
+ def iterlistvalues(self):
+ for key, values in self.iterlists():
+ yield values
+
+ def add(self, key, value):
+ dict.setdefault(self, key, []).append(_omd_bucket(self, key, value))
+
+ def getlist(self, key, type=None):
+ try:
+ rv = dict.__getitem__(self, key)
+ except KeyError:
+ return []
+ if type is None:
+ return [x.value for x in rv]
+ result = []
+ for item in rv:
+ try:
+ result.append(type(item.value))
+ except ValueError:
+ pass
+ return result
+
+ def setlist(self, key, new_list):
+ self.poplist(key)
+ for value in new_list:
+ self.add(key, value)
+
+ def setlistdefault(self, key, default_list=None):
+ raise TypeError('setlistdefault is unsupported for '
+ 'ordered multi dicts')
+
+ def update(self, mapping):
+ for key, value in iter_multi_items(mapping):
+ OrderedMultiDict.add(self, key, value)
+
+ def poplist(self, key):
+ buckets = dict.pop(self, key, ())
+ for bucket in buckets:
+ bucket.unlink(self)
+ return [x.value for x in buckets]
+
+ def pop(self, key, default=_missing):
+ try:
+ buckets = dict.pop(self, key)
+ except KeyError, e:
+ if default is not _missing:
+ return default
+ raise BadRequestKeyError(str(e))
+ for bucket in buckets:
+ bucket.unlink(self)
+ return buckets[0].value
+
+ def popitem(self):
+ try:
+ key, buckets = dict.popitem(self)
+ except KeyError, e:
+ raise BadRequestKeyError(str(e))
+ for bucket in buckets:
+ bucket.unlink(self)
+ return key, buckets[0].value
+
+ def popitemlist(self):
+ try:
+ key, buckets = dict.popitem(self)
+ except KeyError, e:
+ raise BadRequestKeyError(str(e))
+ for bucket in buckets:
+ bucket.unlink(self)
+ return key, [x.value for x in buckets]
+
+
+def _options_header_vkw(value, kw):
+ return dump_options_header(value, dict((k.replace('_', '-'), v)
+ for k, v in kw.items()))
+
+
+class Headers(object):
+ """An object that stores some headers. It has a dict-like interface
+ but is ordered and can store the same keys multiple times.
+
+ This data structure is useful if you want a nicer way to handle WSGI
+ headers which are stored as tuples in a list.
+
+ From Werkzeug 0.3 onwards, the :exc:`KeyError` raised by this class is
+ also a subclass of the :class:`~exceptions.BadRequest` HTTP exception
+ and will render a page for a ``400 BAD REQUEST`` if caught in a
+ catch-all for HTTP exceptions.
+
+ Headers is mostly compatible with the Python :class:`wsgiref.headers.Headers`
+ class, with the exception of `__getitem__`. :mod:`wsgiref` will return
+ `None` for ``headers['missing']``, whereas :class:`Headers` will raise
+ a :class:`KeyError`.
+
+ To create a new :class:`Headers` object pass it a list or dict of headers
+ which are used as default values. This does not reuse the list passed
+ to the constructor for internal usage. To create a :class:`Headers`
+ object that uses as internal storage the list or list-like object you
+ can use the :meth:`linked` class method.
+
+ :param defaults: The list of default values for the :class:`Headers`.
+ """
+
+ def __init__(self, defaults=None, _list=None):
+ if _list is None:
+ _list = []
+ self._list = _list
+ if defaults is not None:
+ if isinstance(defaults, (list, Headers)):
+ self._list.extend(defaults)
+ else:
+ self.extend(defaults)
+
+ @classmethod
+ def linked(cls, headerlist):
+ """Create a new :class:`Headers` object that uses the list of headers
+ passed as internal storage:
+
+ >>> headerlist = [('Content-Length', '40')]
+ >>> headers = Headers.linked(headerlist)
+ >>> headers['Content-Type'] = 'text/html'
+ >>> headerlist
+ [('Content-Length', '40'), ('Content-Type', 'text/html')]
+
+ :param headerlist: The list of headers the class is linked to.
+ :return: new linked :class:`Headers` object.
+ """
+ return cls(_list=headerlist)
+
+ def __getitem__(self, key, _get_mode=False):
+ if not _get_mode:
+ if isinstance(key, (int, long)):
+ return self._list[key]
+ elif isinstance(key, slice):
+ return self.__class__(self._list[key])
+ ikey = key.lower()
+ for k, v in self._list:
+ if k.lower() == ikey:
+ return v
+ # micro optimization: if we are in get mode we will catch that
+ # exception one stack level down so we can raise a standard
+ # key error instead of our special one.
+ if _get_mode:
+ raise KeyError()
+ raise BadRequestKeyError(key)
+
+ def __eq__(self, other):
+ return other.__class__ is self.__class__ and \
+ set(other._list) == set(self._list)
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def get(self, key, default=None, type=None):
+ """Return the default value if the requested data doesn't exist.
+ If `type` is provided and is a callable it should convert the value,
+ return it or raise a :exc:`ValueError` if that is not possible. In
+ this case the function will return the default as if the value was not
+ found:
+
+ >>> d = Headers([('Content-Length', '42')])
+ >>> d.get('Content-Length', type=int)
+ 42
+
+ If a headers object is bound you must not add unicode strings
+ because no encoding takes place.
+
+ :param key: The key to be looked up.
+ :param default: The default value to be returned if the key can't
+ be looked up. If not further specified `None` is
+ returned.
+ :param type: A callable that is used to cast the value in the
+ :class:`Headers`. If a :exc:`ValueError` is raised
+ by this callable the default value is returned.
+ """
+ try:
+ rv = self.__getitem__(key, _get_mode=True)
+ except KeyError:
+ return default
+ if type is None:
+ return rv
+ try:
+ return type(rv)
+ except ValueError:
+ return default
+
+ def getlist(self, key, type=None):
+ """Return the list of items for a given key. If that key is not in the
+ :class:`Headers`, the return value will be an empty list. Just as
+ :meth:`get` :meth:`getlist` accepts a `type` parameter. All items will
+ be converted with the callable defined there.
+
+ :param key: The key to be looked up.
+ :param type: A callable that is used to cast the value in the
+ :class:`Headers`. If a :exc:`ValueError` is raised
+ by this callable the value will be removed from the list.
+ :return: a :class:`list` of all the values for the key.
+ """
+ ikey = key.lower()
+ result = []
+ for k, v in self:
+ if k.lower() == ikey:
+ if type is not None:
+ try:
+ v = type(v)
+ except ValueError:
+ continue
+ result.append(v)
+ return result
+
+ def get_all(self, name):
+ """Return a list of all the values for the named field.
+
+ This method is compatible with the :mod:`wsgiref`
+ :meth:`~wsgiref.headers.Headers.get_all` method.
+ """
+ return self.getlist(name)
+
+ def iteritems(self, lower=False):
+ for key, value in self:
+ if lower:
+ key = key.lower()
+ yield key, value
+
+ def iterkeys(self, lower=False):
+ for key, _ in self.iteritems(lower):
+ yield key
+
+ def itervalues(self):
+ for _, value in self.iteritems():
+ yield value
+
+ def keys(self, lower=False):
+ return list(self.iterkeys(lower))
+
+ def values(self):
+ return list(self.itervalues())
+
+ def items(self, lower=False):
+ return list(self.iteritems(lower))
+
+ def extend(self, iterable):
+ """Extend the headers with a dict or an iterable yielding keys and
+ values.
+ """
+ if isinstance(iterable, dict):
+ for key, value in iterable.iteritems():
+ if isinstance(value, (tuple, list)):
+ for v in value:
+ self.add(key, v)
+ else:
+ self.add(key, value)
+ else:
+ for key, value in iterable:
+ self.add(key, value)
+
+ def __delitem__(self, key, _index_operation=True):
+ if _index_operation and isinstance(key, (int, long, slice)):
+ del self._list[key]
+ return
+ key = key.lower()
+ new = []
+ for k, v in self._list:
+ if k.lower() != key:
+ new.append((k, v))
+ self._list[:] = new
+
+ def remove(self, key):
+ """Remove a key.
+
+ :param key: The key to be removed.
+ """
+ return self.__delitem__(key, _index_operation=False)
+
+ def pop(self, key=None, default=_missing):
+ """Removes and returns a key or index.
+
+ :param key: The key to be popped. If this is an integer the item at
+ that position is removed, if it's a string the value for
+ that key is. If the key is omitted or `None` the last
+ item is removed.
+ :return: an item.
+ """
+ if key is None:
+ return self._list.pop()
+ if isinstance(key, (int, long)):
+ return self._list.pop(key)
+ try:
+ rv = self[key]
+ self.remove(key)
+ except KeyError:
+ if default is not _missing:
+ return default
+ raise
+ return rv
+
+ def popitem(self):
+ """Removes a key or index and returns a (key, value) item."""
+ return self.pop()
+
+ def __contains__(self, key):
+ """Check if a key is present."""
+ try:
+ self.__getitem__(key, _get_mode=True)
+ except KeyError:
+ return False
+ return True
+
+ has_key = __contains__
+
+ def __iter__(self):
+ """Yield ``(key, value)`` tuples."""
+ return iter(self._list)
+
+ def __len__(self):
+ return len(self._list)
+
+ def add(self, _key, _value, **kw):
+ """Add a new header tuple to the list.
+
+ Keyword arguments can specify additional parameters for the header
+ value, with underscores converted to dashes::
+
+ >>> d = Headers()
+ >>> d.add('Content-Type', 'text/plain')
+ >>> d.add('Content-Disposition', 'attachment', filename='foo.png')
+
+ The keyword argument dumping uses :func:`dump_options_header`
+ behind the scenes.
+
+ .. versionadded:: 0.4.1
+ keyword arguments were added for :mod:`wsgiref` compatibility.
+ """
+ if kw:
+ _value = _options_header_vkw(_value, kw)
+ self._validate_value(_value)
+ self._list.append((_key, _value))
+
+ def _validate_value(self, value):
+ if isinstance(value, basestring) and ('\n' in value or '\r' in value):
+ raise ValueError('Detected newline in header value. This is '
+ 'a potential security problem')
+
+ def add_header(self, _key, _value, **_kw):
+ """Add a new header tuple to the list.
+
+ An alias for :meth:`add` for compatibility with the :mod:`wsgiref`
+ :meth:`~wsgiref.headers.Headers.add_header` method.
+ """
+ self.add(_key, _value, **_kw)
+
+ def clear(self):
+ """Clears all headers."""
+ del self._list[:]
+
+ def set(self, _key, _value, **kw):
+ """Remove all header tuples for `key` and add a new one. The newly
+ added key either appears at the end of the list if there was no
+ entry or replaces the first one.
+
+ Keyword arguments can specify additional parameters for the header
+ value, with underscores converted to dashes. See :meth:`add` for
+ more information.
+
+ .. versionchanged:: 0.6.1
+ :meth:`set` now accepts the same arguments as :meth:`add`.
+
+ :param key: The key to be inserted.
+ :param value: The value to be inserted.
+ """
+ if kw:
+ _value = _options_header_vkw(_value, kw)
+ self._validate_value(_value)
+ if not self._list:
+ self._list.append((_key, _value))
+ return
+ listiter = iter(self._list)
+ ikey = _key.lower()
+ for idx, (old_key, old_value) in enumerate(listiter):
+ if old_key.lower() == ikey:
+ # replace first ocurrence
+ self._list[idx] = (_key, _value)
+ break
+ else:
+ self._list.append((_key, _value))
+ return
+ self._list[idx + 1:] = [t for t in listiter if t[0].lower() != ikey]
+
+ def setdefault(self, key, value):
+ """Returns the value for the key if it is in the dict, otherwise it
+ returns `default` and sets that value for `key`.
+
+ :param key: The key to be looked up.
+ :param default: The default value to be returned if the key is not
+ in the dict. If not further specified it's `None`.
+ """
+ if key in self:
+ return self[key]
+ self.set(key, value)
+ return value
+
+ def __setitem__(self, key, value):
+ """Like :meth:`set` but also supports index/slice based setting."""
+ if isinstance(key, (slice, int, long)):
+ self._validate_value(value)
+ self._list[key] = value
+ else:
+ self.set(key, value)
+
+ def to_list(self, charset='iso-8859-1'):
+ """Convert the headers into a list and converts the unicode header
+ items to the specified charset.
+
+ :return: list
+ """
+ return [(k, isinstance(v, unicode) and v.encode(charset) or str(v))
+ for k, v in self]
+
+ def copy(self):
+ return self.__class__(self._list)
+
+ def __copy__(self):
+ return self.copy()
+
+ def __str__(self, charset='iso-8859-1'):
+ """Returns formatted headers suitable for HTTP transmission."""
+ strs = []
+ for key, value in self.to_list(charset):
+ strs.append('%s: %s' % (key, value))
+ strs.append('\r\n')
+ return '\r\n'.join(strs)
+
+ def __repr__(self):
+ return '%s(%r)' % (
+ self.__class__.__name__,
+ list(self)
+ )
+
+
+class ImmutableHeadersMixin(object):
+ """Makes a :class:`Headers` immutable. We do not mark them as
+ hashable though since the only usecase for this datastructure
+ in Werkzeug is a view on a mutable structure.
+
+ .. versionadded:: 0.5
+
+ :private:
+ """
+
+ def __delitem__(self, key):
+ is_immutable(self)
+
+ def __setitem__(self, key, value):
+ is_immutable(self)
+ set = __setitem__
+
+ def add(self, item):
+ is_immutable(self)
+ remove = add_header = add
+
+ def extend(self, iterable):
+ is_immutable(self)
+
+ def insert(self, pos, value):
+ is_immutable(self)
+
+ def pop(self, index=-1):
+ is_immutable(self)
+
+ def popitem(self):
+ is_immutable(self)
+
+ def setdefault(self, key, default):
+ is_immutable(self)
+
+
+class EnvironHeaders(ImmutableHeadersMixin, Headers):
+ """Read only version of the headers from a WSGI environment. This
+ provides the same interface as `Headers` and is constructed from
+ a WSGI environment.
+
+ From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
+ subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
+ render a page for a ``400 BAD REQUEST`` if caught in a catch-all for
+ HTTP exceptions.
+ """
+
+ def __init__(self, environ):
+ self.environ = environ
+
+ @classmethod
+ def linked(cls, environ):
+ raise TypeError('%r object is always linked to environment, '
+ 'no separate initializer' % cls.__name__)
+
+ def __eq__(self, other):
+ return self.environ is other.environ
+
+ def __getitem__(self, key, _get_mode=False):
+ # _get_mode is a no-op for this class as there is no index but
+ # used because get() calls it.
+ key = key.upper().replace('-', '_')
+ if key in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
+ return self.environ[key]
+ return self.environ['HTTP_' + key]
+
+ def __len__(self):
+ # the iter is necessary because otherwise list calls our
+ # len which would call list again and so forth.
+ return len(list(iter(self)))
+
+ def __iter__(self):
+ for key, value in self.environ.iteritems():
+ if key.startswith('HTTP_') and key not in \
+ ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
+ yield key[5:].replace('_', '-').title(), value
+ elif key in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
+ yield key.replace('_', '-').title(), value
+
+ def copy(self):
+ raise TypeError('cannot create %r copies' % self.__class__.__name__)
+
+
+class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict):
+ """A read only :class:`MultiDict` that you can pass multiple :class:`MultiDict`
+ instances as sequence and it will combine the return values of all wrapped
+ dicts:
+
+ >>> from werkzeug.datastructures import CombinedMultiDict, MultiDict
+ >>> post = MultiDict([('foo', 'bar')])
+ >>> get = MultiDict([('blub', 'blah')])
+ >>> combined = CombinedMultiDict([get, post])
+ >>> combined['foo']
+ 'bar'
+ >>> combined['blub']
+ 'blah'
+
+ This works for all read operations and will raise a `TypeError` for
+ methods that usually change data which isn't possible.
+
+ From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
+ subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
+ render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
+ exceptions.
+ """
+
+ def __reduce_ex__(self, protocol):
+ return type(self), (self.dicts,)
+
+ def __init__(self, dicts=None):
+ self.dicts = dicts or []
+
+ @classmethod
+ def fromkeys(cls):
+ raise TypeError('cannot create %r instances by fromkeys' %
+ cls.__name__)
+
+ def __getitem__(self, key):
+ for d in self.dicts:
+ if key in d:
+ return d[key]
+ raise BadRequestKeyError(key)
+
+ def get(self, key, default=None, type=None):
+ for d in self.dicts:
+ if key in d:
+ if type is not None:
+ try:
+ return type(d[key])
+ except ValueError:
+ continue
+ return d[key]
+ return default
+
+ def getlist(self, key, type=None):
+ rv = []
+ for d in self.dicts:
+ rv.extend(d.getlist(key, type))
+ return rv
+
+ def keys(self):
+ rv = set()
+ for d in self.dicts:
+ rv.update(d.keys())
+ return list(rv)
+
+ def iteritems(self, multi=False):
+ found = set()
+ for d in self.dicts:
+ for key, value in d.iteritems(multi):
+ if multi:
+ yield key, value
+ elif key not in found:
+ found.add(key)
+ yield key, value
+
+ def itervalues(self):
+ for key, value in self.iteritems():
+ yield value
+
+ def values(self):
+ return list(self.itervalues())
+
+ def items(self, multi=False):
+ return list(self.iteritems(multi))
+
+ def iterlists(self):
+ rv = {}
+ for d in self.dicts:
+ for key, values in d.iterlists():
+ rv.setdefault(key, []).extend(values)
+ return rv.iteritems()
+
+ def lists(self):
+ return list(self.iterlists())
+
+ def iterlistvalues(self):
+ return (x[0] for x in self.lists())
+
+ def listvalues(self):
+ return list(self.iterlistvalues())
+
+ def iterkeys(self):
+ return iter(self.keys())
+
+ __iter__ = iterkeys
+
+ def copy(self):
+ """Return a shallow copy of this object."""
+ return self.__class__(self.dicts[:])
+
+ def to_dict(self, flat=True):
+ """Return the contents as regular dict. If `flat` is `True` the
+ returned dict will only have the first item present, if `flat` is
+ `False` all values will be returned as lists.
+
+ :param flat: If set to `False` the dict returned will have lists
+ with all the values in it. Otherwise it will only
+ contain the first item for each key.
+ :return: a :class:`dict`
+ """
+ rv = {}
+ for d in reversed(self.dicts):
+ rv.update(d.to_dict(flat))
+ return rv
+
+ def __len__(self):
+ return len(self.keys())
+
+ def __contains__(self, key):
+ for d in self.dicts:
+ if key in d:
+ return True
+ return False
+
+ has_key = __contains__
+
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.dicts)
+
+
+class FileMultiDict(MultiDict):
+ """A special :class:`MultiDict` that has convenience methods to add
+ files to it. This is used for :class:`EnvironBuilder` and generally
+ useful for unittesting.
+
+ .. versionadded:: 0.5
+ """
+
+ def add_file(self, name, file, filename=None, content_type=None):
+ """Adds a new file to the dict. `file` can be a file name or
+ a :class:`file`-like or a :class:`FileStorage` object.
+
+ :param name: the name of the field.
+ :param file: a filename or :class:`file`-like object
+ :param filename: an optional filename
+ :param content_type: an optional content type
+ """
+ if isinstance(file, FileStorage):
+ value = file
+ else:
+ if isinstance(file, basestring):
+ if filename is None:
+ filename = file
+ file = open(file, 'rb')
+ if filename and content_type is None:
+ content_type = mimetypes.guess_type(filename)[0] or \
+ 'application/octet-stream'
+ value = FileStorage(file, filename, name, content_type)
+
+ self.add(name, value)
+
+
+class ImmutableDict(ImmutableDictMixin, dict):
+ """An immutable :class:`dict`.
+
+ .. versionadded:: 0.5
+ """
+
+ __repr__ = _proxy_repr(dict)
+
+ def copy(self):
+ """Return a shallow mutable copy of this object. Keep in mind that
+ the standard library's :func:`copy` function is a no-op for this class
+ like for any other python immutable type (eg: :class:`tuple`).
+ """
+ return dict(self)
+
+ def __copy__(self):
+ return self
+
+
+class ImmutableMultiDict(ImmutableMultiDictMixin, MultiDict):
+ """An immutable :class:`MultiDict`.
+
+ .. versionadded:: 0.5
+ """
+
+ def copy(self):
+ """Return a shallow mutable copy of this object. Keep in mind that
+ the standard library's :func:`copy` function is a no-op for this class
+ like for any other python immutable type (eg: :class:`tuple`).
+ """
+ return MultiDict(self)
+
+ def __copy__(self):
+ return self
+
+
+class ImmutableOrderedMultiDict(ImmutableMultiDictMixin, OrderedMultiDict):
+ """An immutable :class:`OrderedMultiDict`.
+
+ .. versionadded:: 0.6
+ """
+
+ def copy(self):
+ """Return a shallow mutable copy of this object. Keep in mind that
+ the standard library's :func:`copy` function is a no-op for this class
+ like for any other python immutable type (eg: :class:`tuple`).
+ """
+ return OrderedMultiDict(self)
+
+ def __copy__(self):
+ return self
+
+
+class Accept(ImmutableList):
+ """An :class:`Accept` object is just a list subclass for lists of
+ ``(value, quality)`` tuples. It is automatically sorted by quality.
+
+ All :class:`Accept` objects work similar to a list but provide extra
+ functionality for working with the data. Containment checks are
+ normalized to the rules of that header:
+
+ >>> a = CharsetAccept([('ISO-8859-1', 1), ('utf-8', 0.7)])
+ >>> a.best
+ 'ISO-8859-1'
+ >>> 'iso-8859-1' in a
+ True
+ >>> 'UTF8' in a
+ True
+ >>> 'utf7' in a
+ False
+
+ To get the quality for an item you can use normal item lookup:
+
+ >>> print a['utf-8']
+ 0.7
+ >>> a['utf7']
+ 0
+
+ .. versionchanged:: 0.5
+ :class:`Accept` objects are forced immutable now.
+ """
+
+ def __init__(self, values=()):
+ if values is None:
+ list.__init__(self)
+ self.provided = False
+ elif isinstance(values, Accept):
+ self.provided = values.provided
+ list.__init__(self, values)
+ else:
+ self.provided = True
+ values = [(a, b) for b, a in values]
+ values.sort()
+ values.reverse()
+ list.__init__(self, [(a, b) for b, a in values])
+
+ def _value_matches(self, value, item):
+ """Check if a value matches a given accept item."""
+ return item == '*' or item.lower() == value.lower()
+
+ def __getitem__(self, key):
+ """Besides index lookup (getting item n) you can also pass it a string
+ to get the quality for the item. If the item is not in the list, the
+ returned quality is ``0``.
+ """
+ if isinstance(key, basestring):
+ return self.quality(key)
+ return list.__getitem__(self, key)
+
+ def quality(self, key):
+ """Returns the quality of the key.
+
+ .. versionadded:: 0.6
+ In previous versions you had to use the item-lookup syntax
+ (eg: ``obj[key]`` instead of ``obj.quality(key)``)
+ """
+ for item, quality in self:
+ if self._value_matches(key, item):
+ return quality
+ return 0
+
+ def __contains__(self, value):
+ for item, quality in self:
+ if self._value_matches(value, item):
+ return True
+ return False
+
+ def __repr__(self):
+ return '%s([%s])' % (
+ self.__class__.__name__,
+ ', '.join('(%r, %s)' % (x, y) for x, y in self)
+ )
+
+ def index(self, key):
+ """Get the position of an entry or raise :exc:`ValueError`.
+
+ :param key: The key to be looked up.
+
+ .. versionchanged:: 0.5
+ This used to raise :exc:`IndexError`, which was inconsistent
+ with the list API.
+ """
+ if isinstance(key, basestring):
+ for idx, (item, quality) in enumerate(self):
+ if self._value_matches(key, item):
+ return idx
+ raise ValueError(key)
+ return list.index(self, key)
+
+ def find(self, key):
+ """Get the position of an entry or return -1.
+
+ :param key: The key to be looked up.
+ """
+ try:
+ return self.index(key)
+ except ValueError:
+ return -1
+
+ def values(self):
+ """Return a list of the values, not the qualities."""
+ return list(self.itervalues())
+
+ def itervalues(self):
+ """Iterate over all values."""
+ for item in self:
+ yield item[0]
+
+ def to_header(self):
+ """Convert the header set into an HTTP header string."""
+ result = []
+ for value, quality in self:
+ if quality != 1:
+ value = '%s;q=%s' % (value, quality)
+ result.append(value)
+ return ','.join(result)
+
+ def __str__(self):
+ return self.to_header()
+
+ def best_match(self, matches, default=None):
+ """Returns the best match from a list of possible matches based
+ on the quality of the client. If two items have the same quality,
+ the one is returned that comes first.
+
+ :param matches: a list of matches to check for
+ :param default: the value that is returned if none match
+ """
+ best_quality = -1
+ result = default
+ for server_item in matches:
+ for client_item, quality in self:
+ if quality <= best_quality:
+ break
+ if self._value_matches(server_item, client_item):
+ best_quality = quality
+ result = server_item
+ return result
+
+ @property
+ def best(self):
+ """The best match as value."""
+ if self:
+ return self[0][0]
+
+
+class MIMEAccept(Accept):
+ """Like :class:`Accept` but with special methods and behavior for
+ mimetypes.
+ """
+
+ def _value_matches(self, value, item):
+ def _normalize(x):
+ x = x.lower()
+ return x == '*' and ('*', '*') or x.split('/', 1)
+
+ # this is from the application which is trusted. to avoid developer
+ # frustration we actually check these for valid values
+ if '/' not in value:
+ raise ValueError('invalid mimetype %r' % value)
+ value_type, value_subtype = _normalize(value)
+ if value_type == '*' and value_subtype != '*':
+ raise ValueError('invalid mimetype %r' % value)
+
+ if '/' not in item:
+ return False
+ item_type, item_subtype = _normalize(item)
+ if item_type == '*' and item_subtype != '*':
+ return False
+ return (
+ (item_type == item_subtype == '*' or
+ value_type == value_subtype == '*') or
+ (item_type == value_type and (item_subtype == '*' or
+ value_subtype == '*' or
+ item_subtype == value_subtype))
+ )
+
+ @property
+ def accept_html(self):
+ """True if this object accepts HTML."""
+ return (
+ 'text/html' in self or
+ 'application/xhtml+xml' in self or
+ self.accept_xhtml
+ )
+
+ @property
+ def accept_xhtml(self):
+ """True if this object accepts XHTML."""
+ return (
+ 'application/xhtml+xml' in self or
+ 'application/xml' in self
+ )
+
+ @property
+ def accept_json(self):
+ """True if this object accepts JSON."""
+ return 'application/json' in self
+
+
+class LanguageAccept(Accept):
+ """Like :class:`Accept` but with normalization for languages."""
+
+ def _value_matches(self, value, item):
+ def _normalize(language):
+ return _locale_delim_re.split(language.lower())
+ return item == '*' or _normalize(value) == _normalize(item)
+
+
+class CharsetAccept(Accept):
+ """Like :class:`Accept` but with normalization for charsets."""
+
+ def _value_matches(self, value, item):
+ def _normalize(name):
+ try:
+ return codecs.lookup(name).name
+ except LookupError:
+ return name.lower()
+ return item == '*' or _normalize(value) == _normalize(item)
+
+
+def cache_property(key, empty, type):
+ """Return a new property object for a cache header. Useful if you
+ want to add support for a cache extension in a subclass."""
+ return property(lambda x: x._get_cache_value(key, empty, type),
+ lambda x, v: x._set_cache_value(key, v, type),
+ lambda x: x._del_cache_value(key),
+ 'accessor for %r' % key)
+
+
+class _CacheControl(UpdateDictMixin, dict):
+ """Subclass of a dict that stores values for a Cache-Control header. It
+ has accessors for all the cache-control directives specified in RFC 2616.
+ The class does not differentiate between request and response directives.
+
+ Because the cache-control directives in the HTTP header use dashes the
+ python descriptors use underscores for that.
+
+ To get a header of the :class:`CacheControl` object again you can convert
+ the object into a string or call the :meth:`to_header` method. If you plan
+ to subclass it and add your own items have a look at the sourcecode for
+ that class.
+
+ .. versionchanged:: 0.4
+
+ Setting `no_cache` or `private` to boolean `True` will set the implicit
+ none-value which is ``*``:
+
+ >>> cc = ResponseCacheControl()
+ >>> cc.no_cache = True
+ >>> cc
+ <ResponseCacheControl 'no-cache'>
+ >>> cc.no_cache
+ '*'
+ >>> cc.no_cache = None
+ >>> cc
+ <ResponseCacheControl ''>
+
+ In versions before 0.5 the behavior documented here affected the now
+ no longer existing `CacheControl` class.
+ """
+
+ no_cache = cache_property('no-cache', '*', None)
+ no_store = cache_property('no-store', None, bool)
+ max_age = cache_property('max-age', -1, int)
+ no_transform = cache_property('no-transform', None, None)
+
+ def __init__(self, values=(), on_update=None):
+ dict.__init__(self, values or ())
+ self.on_update = on_update
+ self.provided = values is not None
+
+ def _get_cache_value(self, key, empty, type):
+ """Used internally by the accessor properties."""
+ if type is bool:
+ return key in self
+ if key in self:
+ value = self[key]
+ if value is None:
+ return empty
+ elif type is not None:
+ try:
+ value = type(value)
+ except ValueError:
+ pass
+ return value
+
+ def _set_cache_value(self, key, value, type):
+ """Used internally by the accessor properties."""
+ if type is bool:
+ if value:
+ self[key] = None
+ else:
+ self.pop(key, None)
+ else:
+ if value is None:
+ self.pop(key)
+ elif value is True:
+ self[key] = None
+ else:
+ self[key] = value
+
+ def _del_cache_value(self, key):
+ """Used internally by the accessor properties."""
+ if key in self:
+ del self[key]
+
+ def to_header(self):
+ """Convert the stored values into a cache control header."""
+ return dump_header(self)
+
+ def __str__(self):
+ return self.to_header()
+
+ def __repr__(self):
+ return '<%s %r>' % (
+ self.__class__.__name__,
+ self.to_header()
+ )
+
+
+class RequestCacheControl(ImmutableDictMixin, _CacheControl):
+ """A cache control for requests. This is immutable and gives access
+ to all the request-relevant cache control headers.
+
+ To get a header of the :class:`RequestCacheControl` object again you can
+ convert the object into a string or call the :meth:`to_header` method. If
+ you plan to subclass it and add your own items have a look at the sourcecode
+ for that class.
+
+ .. versionadded:: 0.5
+ In previous versions a `CacheControl` class existed that was used
+ both for request and response.
+ """
+
+ max_stale = cache_property('max-stale', '*', int)
+ min_fresh = cache_property('min-fresh', '*', int)
+ no_transform = cache_property('no-transform', None, None)
+ only_if_cached = cache_property('only-if-cached', None, bool)
+
+
+class ResponseCacheControl(_CacheControl):
+ """A cache control for responses. Unlike :class:`RequestCacheControl`
+ this is mutable and gives access to response-relevant cache control
+ headers.
+
+ To get a header of the :class:`ResponseCacheControl` object again you can
+ convert the object into a string or call the :meth:`to_header` method. If
+ you plan to subclass it and add your own items have a look at the sourcecode
+ for that class.
+
+ .. versionadded:: 0.5
+ In previous versions a `CacheControl` class existed that was used
+ both for request and response.
+ """
+
+ public = cache_property('public', None, bool)
+ private = cache_property('private', '*', None)
+ must_revalidate = cache_property('must-revalidate', None, bool)
+ proxy_revalidate = cache_property('proxy-revalidate', None, bool)
+ s_maxage = cache_property('s-maxage', None, None)
+
+
+# attach cache_property to the _CacheControl as staticmethod
+# so that others can reuse it.
+_CacheControl.cache_property = staticmethod(cache_property)
+
+
+class CallbackDict(UpdateDictMixin, dict):
+ """A dict that calls a function passed every time something is changed.
+ The function is passed the dict instance.
+ """
+
+ def __init__(self, initial=None, on_update=None):
+ dict.__init__(self, initial or ())
+ self.on_update = on_update
+
+ def __repr__(self):
+ return '<%s %s>' % (
+ self.__class__.__name__,
+ dict.__repr__(self)
+ )
+
+
+class HeaderSet(object):
+ """Similar to the :class:`ETags` class this implements a set-like structure.
+ Unlike :class:`ETags` this is case insensitive and used for vary, allow, and
+ content-language headers.
+
+ If not constructed using the :func:`parse_set_header` function the
+ instantiation works like this:
+
+ >>> hs = HeaderSet(['foo', 'bar', 'baz'])
+ >>> hs
+ HeaderSet(['foo', 'bar', 'baz'])
+ """
+
+ def __init__(self, headers=None, on_update=None):
+ self._headers = list(headers or ())
+ self._set = set([x.lower() for x in self._headers])
+ self.on_update = on_update
+
+ def add(self, header):
+ """Add a new header to the set."""
+ self.update((header,))
+
+ def remove(self, header):
+ """Remove a header from the set. This raises an :exc:`KeyError` if the
+ header is not in the set.
+
+ .. versionchanged:: 0.5
+ In older versions a :exc:`IndexError` was raised instead of a
+ :exc:`KeyError` if the object was missing.
+
+ :param header: the header to be removed.
+ """
+ key = header.lower()
+ if key not in self._set:
+ raise KeyError(header)
+ self._set.remove(key)
+ for idx, key in enumerate(self._headers):
+ if key.lower() == header:
+ del self._headers[idx]
+ break
+ if self.on_update is not None:
+ self.on_update(self)
+
+ def update(self, iterable):
+ """Add all the headers from the iterable to the set.
+
+ :param iterable: updates the set with the items from the iterable.
+ """
+ inserted_any = False
+ for header in iterable:
+ key = header.lower()
+ if key not in self._set:
+ self._headers.append(header)
+ self._set.add(key)
+ inserted_any = True
+ if inserted_any and self.on_update is not None:
+ self.on_update(self)
+
+ def discard(self, header):
+ """Like :meth:`remove` but ignores errors.
+
+ :param header: the header to be discarded.
+ """
+ try:
+ return self.remove(header)
+ except KeyError:
+ pass
+
+ def find(self, header):
+ """Return the index of the header in the set or return -1 if not found.
+
+ :param header: the header to be looked up.
+ """
+ header = header.lower()
+ for idx, item in enumerate(self._headers):
+ if item.lower() == header:
+ return idx
+ return -1
+
+ def index(self, header):
+ """Return the index of the header in the set or raise an
+ :exc:`IndexError`.
+
+ :param header: the header to be looked up.
+ """
+ rv = self.find(header)
+ if rv < 0:
+ raise IndexError(header)
+ return rv
+
+ def clear(self):
+ """Clear the set."""
+ self._set.clear()
+ del self._headers[:]
+ if self.on_update is not None:
+ self.on_update(self)
+
+ def as_set(self, preserve_casing=False):
+ """Return the set as real python set type. When calling this, all
+ the items are converted to lowercase and the ordering is lost.
+
+ :param preserve_casing: if set to `True` the items in the set returned
+ will have the original case like in the
+ :class:`HeaderSet`, otherwise they will
+ be lowercase.
+ """
+ if preserve_casing:
+ return set(self._headers)
+ return set(self._set)
+
+ def to_header(self):
+ """Convert the header set into an HTTP header string."""
+ return ', '.join(map(quote_header_value, self._headers))
+
+ def __getitem__(self, idx):
+ return self._headers[idx]
+
+ def __delitem__(self, idx):
+ rv = self._headers.pop(idx)
+ self._set.remove(rv.lower())
+ if self.on_update is not None:
+ self.on_update(self)
+
+ def __setitem__(self, idx, value):
+ old = self._headers[idx]
+ self._set.remove(old.lower())
+ self._headers[idx] = value
+ self._set.add(value.lower())
+ if self.on_update is not None:
+ self.on_update(self)
+
+ def __contains__(self, header):
+ return header.lower() in self._set
+
+ def __len__(self):
+ return len(self._set)
+
+ def __iter__(self):
+ return iter(self._headers)
+
+ def __nonzero__(self):
+ return bool(self._set)
+
+ def __str__(self):
+ return self.to_header()
+
+ def __repr__(self):
+ return '%s(%r)' % (
+ self.__class__.__name__,
+ self._headers
+ )
+
+
+class ETags(object):
+ """A set that can be used to check if one etag is present in a collection
+ of etags.
+ """
+
+ def __init__(self, strong_etags=None, weak_etags=None, star_tag=False):
+ self._strong = frozenset(not star_tag and strong_etags or ())
+ self._weak = frozenset(weak_etags or ())
+ self.star_tag = star_tag
+
+ def as_set(self, include_weak=False):
+ """Convert the `ETags` object into a python set. Per default all the
+ weak etags are not part of this set."""
+ rv = set(self._strong)
+ if include_weak:
+ rv.update(self._weak)
+ return rv
+
+ def is_weak(self, etag):
+ """Check if an etag is weak."""
+ return etag in self._weak
+
+ def contains_weak(self, etag):
+ """Check if an etag is part of the set including weak and strong tags."""
+ return self.is_weak(etag) or self.contains(etag)
+
+ def contains(self, etag):
+ """Check if an etag is part of the set ignoring weak tags.
+ It is also possible to use the ``in`` operator.
+
+ """
+ if self.star_tag:
+ return True
+ return etag in self._strong
+
+ def contains_raw(self, etag):
+ """When passed a quoted tag it will check if this tag is part of the
+ set. If the tag is weak it is checked against weak and strong tags,
+ otherwise strong only."""
+ etag, weak = unquote_etag(etag)
+ if weak:
+ return self.contains_weak(etag)
+ return self.contains(etag)
+
+ def to_header(self):
+ """Convert the etags set into a HTTP header string."""
+ if self.star_tag:
+ return '*'
+ return ', '.join(
+ ['"%s"' % x for x in self._strong] +
+ ['w/"%s"' % x for x in self._weak]
+ )
+
+ def __call__(self, etag=None, data=None, include_weak=False):
+ if [etag, data].count(None) != 1:
+ raise TypeError('either tag or data required, but at least one')
+ if etag is None:
+ etag = generate_etag(data)
+ if include_weak:
+ if etag in self._weak:
+ return True
+ return etag in self._strong
+
+ def __nonzero__(self):
+ return bool(self.star_tag or self._strong)
+
+ def __str__(self):
+ return self.to_header()
+
+ def __iter__(self):
+ return iter(self._strong)
+
+ def __contains__(self, etag):
+ return self.contains(etag)
+
+ def __repr__(self):
+ return '<%s %r>' % (self.__class__.__name__, str(self))
+
+
+class IfRange(object):
+ """Very simple object that represents the `If-Range` header in parsed
+ form. It will either have neither a etag or date or one of either but
+ never both.
+
+ .. versionadded:: 0.7
+ """
+
+ def __init__(self, etag=None, date=None):
+ #: The etag parsed and unquoted. Ranges always operate on strong
+ #: etags so the weakness information is not necessary.
+ self.etag = etag
+ #: The date in parsed format or `None`.
+ self.date = date
+
+ def to_header(self):
+ """Converts the object back into an HTTP header."""
+ if self.date is not None:
+ return http_date(self.date)
+ if self.etag is not None:
+ return quote_etag(self.etag)
+ return ''
+
+ def __str__(self):
+ return self.to_header()
+
+ def __repr__(self):
+ return '<%s %r>' % (self.__class__.__name__, str(self))
+
+
+class Range(object):
+ """Represents a range header. All the methods are only supporting bytes
+ as unit. It does store multiple ranges but :meth:`range_for_length` will
+ only work if only one range is provided.
+
+ .. versionadded:: 0.7
+ """
+
+ def __init__(self, units, ranges):
+ #: The units of this range. Usually "bytes".
+ self.units = units
+ #: A list of ``(begin, end)`` tuples for the range header provided.
+ #: The ranges are non-inclusive.
+ self.ranges = ranges
+
+ def range_for_length(self, length):
+ """If the range is for bytes, the length is not None and there is
+ exactly one range and it is satisfiable it returns a ``(start, stop)``
+ tuple, otherwise `None`.
+ """
+ if self.units != 'bytes' or length is None or len(self.ranges) != 1:
+ return None
+ start, end = self.ranges[0]
+ if end is None:
+ end = length
+ if start < 0:
+ start += length
+ if is_byte_range_valid(start, end, length):
+ return start, min(end, length)
+
+ def make_content_range(self, length):
+ """Creates a :class:`~werkzeug.datastructures.ContentRange` object
+ from the current range and given content length.
+ """
+ rng = self.range_for_length(length)
+ if rng is not None:
+ return ContentRange(self.units, rng[0], rng[1], length)
+
+ def to_header(self):
+ """Converts the object back into an HTTP header."""
+ ranges = []
+ for begin, end in self.ranges:
+ if end is None:
+ ranges.append(begin >= 0 and '%s-' % begin or str(begin))
+ else:
+ ranges.append('%s-%s' % (begin, end - 1))
+ return '%s=%s' % (self.units, ','.join(ranges))
+
+ def __str__(self):
+ return self.to_header()
+
+ def __repr__(self):
+ return '<%s %r>' % (self.__class__.__name__, str(self))
+
+
+class ContentRange(object):
+ """Represents the content range header.
+
+ .. versionadded:: 0.7
+ """
+
+ def __init__(self, units, start, stop, length=None, on_update=None):
+ assert is_byte_range_valid(start, stop, length), \
+ 'Bad range provided'
+ self.on_update = on_update
+ self.set(start, stop, length, units)
+
+ def _callback_property(name):
+ def fget(self):
+ return getattr(self, name)
+ def fset(self, value):
+ setattr(self, name, value)
+ if self.on_update is not None:
+ self.on_update(self)
+ return property(fget, fset)
+
+ #: The units to use, usually "bytes"
+ units = _callback_property('_units')
+ #: The start point of the range or `None`.
+ start = _callback_property('_start')
+ #: The stop point of the range (non-inclusive) or `None`. Can only be
+ #: `None` if also start is `None`.
+ stop = _callback_property('_stop')
+ #: The length of the range or `None`.
+ length = _callback_property('_length')
+
+ def set(self, start, stop, length=None, units='bytes'):
+ """Simple method to update the ranges."""
+ assert is_byte_range_valid(start, stop, length), \
+ 'Bad range provided'
+ self._units = units
+ self._start = start
+ self._stop = stop
+ self._length = length
+ if self.on_update is not None:
+ self.on_update(self)
+
+ def unset(self):
+ """Sets the units to `None` which indicates that the header should
+ no longer be used.
+ """
+ self.set(None, None, units=None)
+
+ def to_header(self):
+ if self.units is None:
+ return ''
+ if self.length is None:
+ length = '*'
+ else:
+ length = self.length
+ if self.start is None:
+ return '%s */%s' % (self.units, length)
+ return '%s %s-%s/%s' % (
+ self.units,
+ self.start,
+ self.stop - 1,
+ length
+ )
+
+ def __nonzero__(self):
+ return self.units is not None
+
+ def __str__(self):
+ return self.to_header()
+
+ def __repr__(self):
+ return '<%s %r>' % (self.__class__.__name__, str(self))
+
+
+class Authorization(ImmutableDictMixin, dict):
+ """Represents an `Authorization` header sent by the client. You should
+ not create this kind of object yourself but use it when it's returned by
+ the `parse_authorization_header` function.
+
+ This object is a dict subclass and can be altered by setting dict items
+ but it should be considered immutable as it's returned by the client and
+ not meant for modifications.
+
+ .. versionchanged:: 0.5
+ This object became immutable.
+ """
+
+ def __init__(self, auth_type, data=None):
+ dict.__init__(self, data or {})
+ self.type = auth_type
+
+ username = property(lambda x: x.get('username'), doc='''
+ The username transmitted. This is set for both basic and digest
+ auth all the time.''')
+ password = property(lambda x: x.get('password'), doc='''
+ When the authentication type is basic this is the password
+ transmitted by the client, else `None`.''')
+ realm = property(lambda x: x.get('realm'), doc='''
+ This is the server realm sent back for HTTP digest auth.''')
+ nonce = property(lambda x: x.get('nonce'), doc='''
+ The nonce the server sent for digest auth, sent back by the client.
+ A nonce should be unique for every 401 response for HTTP digest
+ auth.''')
+ uri = property(lambda x: x.get('uri'), doc='''
+ The URI from Request-URI of the Request-Line; duplicated because
+ proxies are allowed to change the Request-Line in transit. HTTP
+ digest auth only.''')
+ nc = property(lambda x: x.get('nc'), doc='''
+ The nonce count value transmitted by clients if a qop-header is
+ also transmitted. HTTP digest auth only.''')
+ cnonce = property(lambda x: x.get('cnonce'), doc='''
+ If the server sent a qop-header in the ``WWW-Authenticate``
+ header, the client has to provide this value for HTTP digest auth.
+ See the RFC for more details.''')
+ response = property(lambda x: x.get('response'), doc='''
+ A string of 32 hex digits computed as defined in RFC 2617, which
+ proves that the user knows a password. Digest auth only.''')
+ opaque = property(lambda x: x.get('opaque'), doc='''
+ The opaque header from the server returned unchanged by the client.
+ It is recommended that this string be base64 or hexadecimal data.
+ Digest auth only.''')
+
+ @property
+ def qop(self):
+ """Indicates what "quality of protection" the client has applied to
+ the message for HTTP digest auth."""
+ def on_update(header_set):
+ if not header_set and 'qop' in self:
+ del self['qop']
+ elif header_set:
+ self['qop'] = header_set.to_header()
+ return parse_set_header(self.get('qop'), on_update)
+
+
+class WWWAuthenticate(UpdateDictMixin, dict):
+ """Provides simple access to `WWW-Authenticate` headers."""
+
+ #: list of keys that require quoting in the generated header
+ _require_quoting = frozenset(['domain', 'nonce', 'opaque', 'realm'])
+
+ def __init__(self, auth_type=None, values=None, on_update=None):
+ dict.__init__(self, values or ())
+ if auth_type:
+ self['__auth_type__'] = auth_type
+ self.on_update = on_update
+
+ def set_basic(self, realm='authentication required'):
+ """Clear the auth info and enable basic auth."""
+ dict.clear(self)
+ dict.update(self, {'__auth_type__': 'basic', 'realm': realm})
+ if self.on_update:
+ self.on_update(self)
+
+ def set_digest(self, realm, nonce, qop=('auth',), opaque=None,
+ algorithm=None, stale=False):
+ """Clear the auth info and enable digest auth."""
+ d = {
+ '__auth_type__': 'digest',
+ 'realm': realm,
+ 'nonce': nonce,
+ 'qop': dump_header(qop)
+ }
+ if stale:
+ d['stale'] = 'TRUE'
+ if opaque is not None:
+ d['opaque'] = opaque
+ if algorithm is not None:
+ d['algorithm'] = algorithm
+ dict.clear(self)
+ dict.update(self, d)
+ if self.on_update:
+ self.on_update(self)
+
+ def to_header(self):
+ """Convert the stored values into a WWW-Authenticate header."""
+ d = dict(self)
+ auth_type = d.pop('__auth_type__', None) or 'basic'
+ return '%s %s' % (auth_type.title(), ', '.join([
+ '%s=%s' % (key, quote_header_value(value,
+ allow_token=key not in self._require_quoting))
+ for key, value in d.iteritems()
+ ]))
+
+ def __str__(self):
+ return self.to_header()
+
+ def __repr__(self):
+ return '<%s %r>' % (
+ self.__class__.__name__,
+ self.to_header()
+ )
+
+ def auth_property(name, doc=None):
+ """A static helper function for subclasses to add extra authentication
+ system properties onto a class::
+
+ class FooAuthenticate(WWWAuthenticate):
+ special_realm = auth_property('special_realm')
+
+ For more information have a look at the sourcecode to see how the
+ regular properties (:attr:`realm` etc.) are implemented.
+ """
+ def _set_value(self, value):
+ if value is None:
+ self.pop(name, None)
+ else:
+ self[name] = str(value)
+ return property(lambda x: x.get(name), _set_value, doc=doc)
+
+ def _set_property(name, doc=None):
+ def fget(self):
+ def on_update(header_set):
+ if not header_set and name in self:
+ del self[name]
+ elif header_set:
+ self[name] = header_set.to_header()
+ return parse_set_header(self.get(name), on_update)
+ return property(fget, doc=doc)
+
+ type = auth_property('__auth_type__', doc='''
+ The type of the auth mechanism. HTTP currently specifies
+ `Basic` and `Digest`.''')
+ realm = auth_property('realm', doc='''
+ A string to be displayed to users so they know which username and
+ password to use. This string should contain at least the name of
+ the host performing the authentication and might additionally
+ indicate the collection of users who might have access.''')
+ domain = _set_property('domain', doc='''
+ A list of URIs that define the protection space. If a URI is an
+ absolute path, it is relative to the canonical root URL of the
+ server being accessed.''')
+ nonce = auth_property('nonce', doc='''
+ A server-specified data string which should be uniquely generated
+ each time a 401 response is made. It is recommended that this
+ string be base64 or hexadecimal data.''')
+ opaque = auth_property('opaque', doc='''
+ A string of data, specified by the server, which should be returned
+ by the client unchanged in the Authorization header of subsequent
+ requests with URIs in the same protection space. It is recommended
+ that this string be base64 or hexadecimal data.''')
+ algorithm = auth_property('algorithm', doc='''
+ A string indicating a pair of algorithms used to produce the digest
+ and a checksum. If this is not present it is assumed to be "MD5".
+ If the algorithm is not understood, the challenge should be ignored
+ (and a different one used, if there is more than one).''')
+ qop = _set_property('qop', doc='''
+ A set of quality-of-privacy directives such as auth and auth-int.''')
+
+ def _get_stale(self):
+ val = self.get('stale')
+ if val is not None:
+ return val.lower() == 'true'
+ def _set_stale(self, value):
+ if value is None:
+ self.pop('stale', None)
+ else:
+ self['stale'] = value and 'TRUE' or 'FALSE'
+ stale = property(_get_stale, _set_stale, doc='''
+ A flag, indicating that the previous request from the client was
+ rejected because the nonce value was stale.''')
+ del _get_stale, _set_stale
+
+ # make auth_property a staticmethod so that subclasses of
+ # `WWWAuthenticate` can use it for new properties.
+ auth_property = staticmethod(auth_property)
+ del _set_property
+
+
+class FileStorage(object):
+ """The :class:`FileStorage` class is a thin wrapper over incoming files.
+ It is used by the request object to represent uploaded files. All the
+ attributes of the wrapper stream are proxied by the file storage so
+ it's possible to do ``storage.read()`` instead of the long form
+ ``storage.stream.read()``.
+ """
+
+ def __init__(self, stream=None, filename=None, name=None,
+ content_type=None, content_length=None,
+ headers=None):
+ self.name = name
+ self.stream = stream or _empty_stream
+
+ # if no filename is provided we can attempt to get the filename
+ # from the stream object passed. There we have to be careful to
+ # skip things like <fdopen>, <stderr> etc. Python marks these
+ # special filenames with angular brackets.
+ if filename is None:
+ filename = getattr(stream, 'name', None)
+ if filename and filename[0] == '<' and filename[-1] == '>':
+ filename = None
+
+ self.filename = filename
+ if headers is None:
+ headers = Headers()
+ self.headers = headers
+ if content_type is not None:
+ headers['Content-Type'] = content_type
+ if content_length is not None:
+ headers['Content-Length'] = str(content_length)
+
+ def _parse_content_type(self):
+ if not hasattr(self, '_parsed_content_type'):
+ self._parsed_content_type = \
+ parse_options_header(self.content_type)
+
+ @property
+ def content_type(self):
+ """The file's content type. Usually not available"""
+ return self.headers.get('content-type')
+
+ @property
+ def content_length(self):
+ """The file's content length. Usually not available"""
+ return int(self.headers.get('content-length') or 0)
+
+ @property
+ def mimetype(self):
+ """Like :attr:`content_type` but without parameters (eg, without
+ charset, type etc.). For example if the content
+ type is ``text/html; charset=utf-8`` the mimetype would be
+ ``'text/html'``.
+
+ .. versionadded:: 0.7
+ """
+ self._parse_content_type()
+ return self._parsed_content_type[0]
+
+ @property
+ def mimetype_params(self):
+ """The mimetype parameters as dict. For example if the content
+ type is ``text/html; charset=utf-8`` the params would be
+ ``{'charset': 'utf-8'}``.
+
+ .. versionadded:: 0.7
+ """
+ self._parse_content_type()
+ return self._parsed_content_type[1]
+
+ def save(self, dst, buffer_size=16384):
+ """Save the file to a destination path or file object. If the
+ destination is a file object you have to close it yourself after the
+ call. The buffer size is the number of bytes held in memory during
+ the copy process. It defaults to 16KB.
+
+ For secure file saving also have a look at :func:`secure_filename`.
+
+ :param dst: a filename or open file object the uploaded file
+ is saved to.
+ :param buffer_size: the size of the buffer. This works the same as
+ the `length` parameter of
+ :func:`shutil.copyfileobj`.
+ """
+ from shutil import copyfileobj
+ close_dst = False
+ if isinstance(dst, basestring):
+ dst = file(dst, 'wb')
+ close_dst = True
+ try:
+ copyfileobj(self.stream, dst, buffer_size)
+ finally:
+ if close_dst:
+ dst.close()
+
+ def close(self):
+ """Close the underlying file if possible."""
+ try:
+ self.stream.close()
+ except Exception:
+ pass
+
+ def __nonzero__(self):
+ return bool(self.filename)
+
+ def __getattr__(self, name):
+ return getattr(self.stream, name)
+
+ def __iter__(self):
+ return iter(self.readline, '')
+
+ def __repr__(self):
+ return '<%s: %r (%r)>' % (
+ self.__class__.__name__,
+ self.filename,
+ self.content_type
+ )
+
+
+# circular dependencies
+from werkzeug.http import dump_options_header, dump_header, generate_etag, \
+ quote_header_value, parse_set_header, unquote_etag, quote_etag, \
+ parse_options_header, http_date, is_byte_range_valid
+from werkzeug.exceptions import BadRequestKeyError
diff --git a/websdk/werkzeug/debug/__init__.py b/websdk/werkzeug/debug/__init__.py
new file mode 100644
index 0000000..db91b74
--- /dev/null
+++ b/websdk/werkzeug/debug/__init__.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.debug
+ ~~~~~~~~~~~~~~
+
+ WSGI application traceback debugger.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import mimetypes
+from os.path import join, dirname, basename, isfile
+from werkzeug.wrappers import BaseRequest as Request, BaseResponse as Response
+from werkzeug.debug.tbtools import get_current_traceback, render_console_html
+from werkzeug.debug.console import Console
+from werkzeug.security import gen_salt
+
+
+#: import this here because it once was documented as being available
+#: from this module. In case there are users left ...
+from werkzeug.debug.repr import debug_repr
+
+
+class _ConsoleFrame(object):
+ """Helper class so that we can reuse the frame console code for the
+ standalone console.
+ """
+
+ def __init__(self, namespace):
+ self.console = Console(namespace)
+ self.id = 0
+
+
+class DebuggedApplication(object):
+ """Enables debugging support for a given application::
+
+ from werkzeug.debug import DebuggedApplication
+ from myapp import app
+ app = DebuggedApplication(app, evalex=True)
+
+ The `evalex` keyword argument allows evaluating expressions in a
+ traceback's frame context.
+
+ .. versionadded:: 0.7
+ The `lodgeit_url` parameter was added.
+
+ :param app: the WSGI application to run debugged.
+ :param evalex: enable exception evaluation feature (interactive
+ debugging). This requires a non-forking server.
+ :param request_key: The key that points to the request object in ths
+ environment. This parameter is ignored in current
+ versions.
+ :param console_path: the URL for a general purpose console.
+ :param console_init_func: the function that is executed before starting
+ the general purpose console. The return value
+ is used as initial namespace.
+ :param show_hidden_frames: by default hidden traceback frames are skipped.
+ You can show them by setting this parameter
+ to `True`.
+ :param lodgeit_url: the base URL of the LodgeIt instance to use for
+ pasting tracebacks.
+ """
+
+ # this class is public
+ __module__ = 'werkzeug'
+
+ def __init__(self, app, evalex=False, request_key='werkzeug.request',
+ console_path='/console', console_init_func=None,
+ show_hidden_frames=False,
+ lodgeit_url='http://paste.pocoo.org/'):
+ if not console_init_func:
+ console_init_func = dict
+ self.app = app
+ self.evalex = evalex
+ self.frames = {}
+ self.tracebacks = {}
+ self.request_key = request_key
+ self.console_path = console_path
+ self.console_init_func = console_init_func
+ self.show_hidden_frames = show_hidden_frames
+ self.lodgeit_url = lodgeit_url
+ self.secret = gen_salt(20)
+
+ def debug_application(self, environ, start_response):
+ """Run the application and conserve the traceback frames."""
+ app_iter = None
+ try:
+ app_iter = self.app(environ, start_response)
+ for item in app_iter:
+ yield item
+ if hasattr(app_iter, 'close'):
+ app_iter.close()
+ except Exception:
+ if hasattr(app_iter, 'close'):
+ app_iter.close()
+ traceback = get_current_traceback(skip=1, show_hidden_frames=
+ self.show_hidden_frames,
+ ignore_system_exceptions=True)
+ for frame in traceback.frames:
+ self.frames[frame.id] = frame
+ self.tracebacks[traceback.id] = traceback
+
+ try:
+ start_response('500 INTERNAL SERVER ERROR', [
+ ('Content-Type', 'text/html; charset=utf-8')
+ ])
+ except Exception:
+ # if we end up here there has been output but an error
+ # occurred. in that situation we can do nothing fancy any
+ # more, better log something into the error log and fall
+ # back gracefully.
+ environ['wsgi.errors'].write(
+ 'Debugging middleware caught exception in streamed '
+ 'response at a point where response headers were already '
+ 'sent.\n')
+ else:
+ yield traceback.render_full(evalex=self.evalex,
+ lodgeit_url=self.lodgeit_url,
+ secret=self.secret) \
+ .encode('utf-8', 'replace')
+
+ traceback.log(environ['wsgi.errors'])
+
+ def execute_command(self, request, command, frame):
+ """Execute a command in a console."""
+ return Response(frame.console.eval(command), mimetype='text/html')
+
+ def display_console(self, request):
+ """Display a standalone shell."""
+ if 0 not in self.frames:
+ self.frames[0] = _ConsoleFrame(self.console_init_func())
+ return Response(render_console_html(secret=self.secret),
+ mimetype='text/html')
+
+ def paste_traceback(self, request, traceback):
+ """Paste the traceback and return a JSON response."""
+ paste_id = traceback.paste(self.lodgeit_url)
+ return Response('{"url": "%sshow/%s/", "id": "%s"}'
+ % (self.lodgeit_url, paste_id, paste_id),
+ mimetype='application/json')
+
+ def get_source(self, request, frame):
+ """Render the source viewer."""
+ return Response(frame.render_source(), mimetype='text/html')
+
+ def get_resource(self, request, filename):
+ """Return a static resource from the shared folder."""
+ filename = join(dirname(__file__), 'shared', basename(filename))
+ if isfile(filename):
+ mimetype = mimetypes.guess_type(filename)[0] \
+ or 'application/octet-stream'
+ f = file(filename, 'rb')
+ try:
+ return Response(f.read(), mimetype=mimetype)
+ finally:
+ f.close()
+ return Response('Not Found', status=404)
+
+ def __call__(self, environ, start_response):
+ """Dispatch the requests."""
+ # important: don't ever access a function here that reads the incoming
+ # form data! Otherwise the application won't have access to that data
+ # any more!
+ request = Request(environ)
+ response = self.debug_application
+ if request.args.get('__debugger__') == 'yes':
+ cmd = request.args.get('cmd')
+ arg = request.args.get('f')
+ secret = request.args.get('s')
+ traceback = self.tracebacks.get(request.args.get('tb', type=int))
+ frame = self.frames.get(request.args.get('frm', type=int))
+ if cmd == 'resource' and arg:
+ response = self.get_resource(request, arg)
+ elif cmd == 'paste' and traceback is not None and \
+ secret == self.secret:
+ response = self.paste_traceback(request, traceback)
+ elif cmd == 'source' and frame and self.secret == secret:
+ response = self.get_source(request, frame)
+ elif self.evalex and cmd is not None and frame is not None and \
+ self.secret == secret:
+ response = self.execute_command(request, cmd, frame)
+ elif self.evalex and self.console_path is not None and \
+ request.path == self.console_path:
+ response = self.display_console(request)
+ return response(environ, start_response)
diff --git a/websdk/werkzeug/debug/console.py b/websdk/werkzeug/debug/console.py
new file mode 100644
index 0000000..aace8c6
--- /dev/null
+++ b/websdk/werkzeug/debug/console.py
@@ -0,0 +1,204 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.debug.console
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Interactive console support.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD.
+"""
+import sys
+import code
+from types import CodeType
+from werkzeug.utils import escape
+from werkzeug.local import Local
+from werkzeug.debug.repr import debug_repr, dump, helper
+
+
+_local = Local()
+
+
+class HTMLStringO(object):
+ """A StringO version that HTML escapes on write."""
+
+ def __init__(self):
+ self._buffer = []
+
+ def isatty(self):
+ return False
+
+ def close(self):
+ pass
+
+ def flush(self):
+ pass
+
+ def seek(self, n, mode=0):
+ pass
+
+ def readline(self):
+ if len(self._buffer) == 0:
+ return ''
+ ret = self._buffer[0]
+ del self._buffer[0]
+ return ret
+
+ def reset(self):
+ val = ''.join(self._buffer)
+ del self._buffer[:]
+ return val
+
+ def _write(self, x):
+ if isinstance(x, str):
+ x = x.decode('utf-8', 'replace')
+ self._buffer.append(x)
+
+ def write(self, x):
+ self._write(escape(x))
+
+ def writelines(self, x):
+ self._write(escape(''.join(x)))
+
+
+class ThreadedStream(object):
+ """Thread-local wrapper for sys.stdout for the interactive console."""
+
+ def push():
+ if not isinstance(sys.stdout, ThreadedStream):
+ sys.stdout = ThreadedStream()
+ _local.stream = HTMLStringO()
+ push = staticmethod(push)
+
+ def fetch():
+ try:
+ stream = _local.stream
+ except AttributeError:
+ return ''
+ return stream.reset()
+ fetch = staticmethod(fetch)
+
+ def displayhook(obj):
+ try:
+ stream = _local.stream
+ except AttributeError:
+ return _displayhook(obj)
+ # stream._write bypasses escaping as debug_repr is
+ # already generating HTML for us.
+ if obj is not None:
+ stream._write(debug_repr(obj))
+ displayhook = staticmethod(displayhook)
+
+ def __setattr__(self, name, value):
+ raise AttributeError('read only attribute %s' % name)
+
+ def __dir__(self):
+ return dir(sys.__stdout__)
+
+ def __getattribute__(self, name):
+ if name == '__members__':
+ return dir(sys.__stdout__)
+ try:
+ stream = _local.stream
+ except AttributeError:
+ stream = sys.__stdout__
+ return getattr(stream, name)
+
+ def __repr__(self):
+ return repr(sys.__stdout__)
+
+
+# add the threaded stream as display hook
+_displayhook = sys.displayhook
+sys.displayhook = ThreadedStream.displayhook
+
+
+class _ConsoleLoader(object):
+
+ def __init__(self):
+ self._storage = {}
+
+ def register(self, code, source):
+ self._storage[id(code)] = source
+ # register code objects of wrapped functions too.
+ for var in code.co_consts:
+ if isinstance(var, CodeType):
+ self._storage[id(var)] = source
+
+ def get_source_by_code(self, code):
+ try:
+ return self._storage[id(code)]
+ except KeyError:
+ pass
+
+
+def _wrap_compiler(console):
+ compile = console.compile
+ def func(source, filename, symbol):
+ code = compile(source, filename, symbol)
+ console.loader.register(code, source)
+ return code
+ console.compile = func
+
+
+class _InteractiveConsole(code.InteractiveInterpreter):
+
+ def __init__(self, globals, locals):
+ code.InteractiveInterpreter.__init__(self, locals)
+ self.globals = dict(globals)
+ self.globals['dump'] = dump
+ self.globals['help'] = helper
+ self.globals['__loader__'] = self.loader = _ConsoleLoader()
+ self.more = False
+ self.buffer = []
+ _wrap_compiler(self)
+
+ def runsource(self, source):
+ source = source.rstrip() + '\n'
+ ThreadedStream.push()
+ prompt = self.more and '... ' or '>>> '
+ try:
+ source_to_eval = ''.join(self.buffer + [source])
+ if code.InteractiveInterpreter.runsource(self,
+ source_to_eval, '<debugger>', 'single'):
+ self.more = True
+ self.buffer.append(source)
+ else:
+ self.more = False
+ del self.buffer[:]
+ finally:
+ output = ThreadedStream.fetch()
+ return prompt + source + output
+
+ def runcode(self, code):
+ try:
+ exec code in self.globals, self.locals
+ except Exception:
+ self.showtraceback()
+
+ def showtraceback(self):
+ from werkzeug.debug.tbtools import get_current_traceback
+ tb = get_current_traceback(skip=1)
+ sys.stdout._write(tb.render_summary())
+
+ def showsyntaxerror(self, filename=None):
+ from werkzeug.debug.tbtools import get_current_traceback
+ tb = get_current_traceback(skip=4)
+ sys.stdout._write(tb.render_summary())
+
+ def write(self, data):
+ sys.stdout.write(data)
+
+
+class Console(object):
+ """An interactive console."""
+
+ def __init__(self, globals=None, locals=None):
+ if locals is None:
+ locals = {}
+ if globals is None:
+ globals = {}
+ self._ipy = _InteractiveConsole(globals, locals)
+
+ def eval(self, code):
+ return self._ipy.runsource(code)
diff --git a/websdk/werkzeug/debug/repr.py b/websdk/werkzeug/debug/repr.py
new file mode 100644
index 0000000..a6a54a9
--- /dev/null
+++ b/websdk/werkzeug/debug/repr.py
@@ -0,0 +1,267 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.debug.repr
+ ~~~~~~~~~~~~~~~~~~~
+
+ This module implements object representations for debugging purposes.
+ Unlike the default repr these reprs expose a lot more information and
+ produce HTML instead of ASCII.
+
+ Together with the CSS and JavaScript files of the debugger this gives
+ a colorful and more compact output.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD.
+"""
+import sys
+import re
+from traceback import format_exception_only
+try:
+ from collections import deque
+except ImportError: # pragma: no cover
+ deque = None
+from werkzeug.utils import escape
+
+
+missing = object()
+_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
+RegexType = type(_paragraph_re)
+
+
+HELP_HTML = '''\
+<div class=box>
+ <h3>%(title)s</h3>
+ <pre class=help>%(text)s</pre>
+</div>\
+'''
+OBJECT_DUMP_HTML = '''\
+<div class=box>
+ <h3>%(title)s</h3>
+ %(repr)s
+ <table>%(items)s</table>
+</div>\
+'''
+
+
+def debug_repr(obj):
+ """Creates a debug repr of an object as HTML unicode string."""
+ return DebugReprGenerator().repr(obj)
+
+
+def dump(obj=missing):
+ """Print the object details to stdout._write (for the interactive
+ console of the web debugger.
+ """
+ gen = DebugReprGenerator()
+ if obj is missing:
+ rv = gen.dump_locals(sys._getframe(1).f_locals)
+ else:
+ rv = gen.dump_object(obj)
+ sys.stdout._write(rv)
+
+
+class _Helper(object):
+ """Displays an HTML version of the normal help, for the interactive
+ debugger only because it requires a patched sys.stdout.
+ """
+
+ def __repr__(self):
+ return 'Type help(object) for help about object.'
+
+ def __call__(self, topic=None):
+ if topic is None:
+ sys.stdout._write('<span class=help>%s</span>' % repr(self))
+ return
+ import pydoc
+ pydoc.help(topic)
+ rv = sys.stdout.reset().decode('utf-8', 'ignore')
+ paragraphs = _paragraph_re.split(rv)
+ if len(paragraphs) > 1:
+ title = paragraphs[0]
+ text = '\n\n'.join(paragraphs[1:])
+ else: # pragma: no cover
+ title = 'Help'
+ text = paragraphs[0]
+ sys.stdout._write(HELP_HTML % {'title': title, 'text': text})
+
+
+helper = _Helper()
+
+
+def _add_subclass_info(inner, obj, base):
+ if isinstance(base, tuple):
+ for base in base:
+ if type(obj) is base:
+ return inner
+ elif type(obj) is base:
+ return inner
+ module = ''
+ if obj.__class__.__module__ not in ('__builtin__', 'exceptions'):
+ module = '<span class="module">%s.</span>' % obj.__class__.__module__
+ return '%s%s(%s)' % (module, obj.__class__.__name__, inner)
+
+
+class DebugReprGenerator(object):
+
+ def __init__(self):
+ self._stack = []
+
+ def _sequence_repr_maker(left, right, base=object(), limit=8):
+ def proxy(self, obj, recursive):
+ if recursive:
+ return _add_subclass_info(left + '...' + right, obj, base)
+ buf = [left]
+ have_extended_section = False
+ for idx, item in enumerate(obj):
+ if idx:
+ buf.append(', ')
+ if idx == limit:
+ buf.append('<span class="extended">')
+ have_extended_section = True
+ buf.append(self.repr(item))
+ if have_extended_section:
+ buf.append('</span>')
+ buf.append(right)
+ return _add_subclass_info(u''.join(buf), obj, base)
+ return proxy
+
+ list_repr = _sequence_repr_maker('[', ']', list)
+ tuple_repr = _sequence_repr_maker('(', ')', tuple)
+ set_repr = _sequence_repr_maker('set([', '])', set)
+ frozenset_repr = _sequence_repr_maker('frozenset([', '])', frozenset)
+ if deque is not None:
+ deque_repr = _sequence_repr_maker('<span class="module">collections.'
+ '</span>deque([', '])', deque)
+ del _sequence_repr_maker
+
+ def regex_repr(self, obj):
+ pattern = repr(obj.pattern).decode('string-escape', 'ignore')
+ if pattern[:1] == 'u':
+ pattern = 'ur' + pattern[1:]
+ else:
+ pattern = 'r' + pattern
+ return u're.compile(<span class="string regex">%s</span>)' % pattern
+
+ def string_repr(self, obj, limit=70):
+ buf = ['<span class="string">']
+ escaped = escape(obj)
+ a = repr(escaped[:limit])
+ b = repr(escaped[limit:])
+ if isinstance(obj, unicode):
+ buf.append('u')
+ a = a[1:]
+ b = b[1:]
+ if b != "''":
+ buf.extend((a[:-1], '<span class="extended">', b[1:], '</span>'))
+ else:
+ buf.append(a)
+ buf.append('</span>')
+ return _add_subclass_info(u''.join(buf), obj, (str, unicode))
+
+ def dict_repr(self, d, recursive, limit=5):
+ if recursive:
+ return _add_subclass_info(u'{...}', d, dict)
+ buf = ['{']
+ have_extended_section = False
+ for idx, (key, value) in enumerate(d.iteritems()):
+ if idx:
+ buf.append(', ')
+ if idx == limit - 1:
+ buf.append('<span class="extended">')
+ have_extended_section = True
+ buf.append('<span class="pair"><span class="key">%s</span>: '
+ '<span class="value">%s</span></span>' %
+ (self.repr(key), self.repr(value)))
+ if have_extended_section:
+ buf.append('</span>')
+ buf.append('}')
+ return _add_subclass_info(u''.join(buf), d, dict)
+
+ def object_repr(self, obj):
+ return u'<span class="object">%s</span>' % \
+ escape(repr(obj).decode('utf-8', 'replace'))
+
+ def dispatch_repr(self, obj, recursive):
+ if obj is helper:
+ return u'<span class="help">%r</span>' % helper
+ if isinstance(obj, (int, long, float, complex)):
+ return u'<span class="number">%r</span>' % obj
+ if isinstance(obj, basestring):
+ return self.string_repr(obj)
+ if isinstance(obj, RegexType):
+ return self.regex_repr(obj)
+ if isinstance(obj, list):
+ return self.list_repr(obj, recursive)
+ if isinstance(obj, tuple):
+ return self.tuple_repr(obj, recursive)
+ if isinstance(obj, set):
+ return self.set_repr(obj, recursive)
+ if isinstance(obj, frozenset):
+ return self.frozenset_repr(obj, recursive)
+ if isinstance(obj, dict):
+ return self.dict_repr(obj, recursive)
+ if deque is not None and isinstance(obj, deque):
+ return self.deque_repr(obj, recursive)
+ return self.object_repr(obj)
+
+ def fallback_repr(self):
+ try:
+ info = ''.join(format_exception_only(*sys.exc_info()[:2]))
+ except Exception: # pragma: no cover
+ info = '?'
+ return u'<span class="brokenrepr">&lt;broken repr (%s)&gt;' \
+ u'</span>' % escape(info.decode('utf-8', 'ignore').strip())
+
+ def repr(self, obj):
+ recursive = False
+ for item in self._stack:
+ if item is obj:
+ recursive = True
+ break
+ self._stack.append(obj)
+ try:
+ try:
+ return self.dispatch_repr(obj, recursive)
+ except Exception:
+ return self.fallback_repr()
+ finally:
+ self._stack.pop()
+
+ def dump_object(self, obj):
+ repr = items = None
+ if isinstance(obj, dict):
+ title = 'Contents of'
+ items = []
+ for key, value in obj.iteritems():
+ if not isinstance(key, basestring):
+ items = None
+ break
+ items.append((key, self.repr(value)))
+ if items is None:
+ items = []
+ repr = self.repr(obj)
+ for key in dir(obj):
+ try:
+ items.append((key, self.repr(getattr(obj, key))))
+ except Exception:
+ pass
+ title = 'Details for'
+ title += ' ' + object.__repr__(obj)[1:-1]
+ return self.render_object_dump(items, title, repr)
+
+ def dump_locals(self, d):
+ items = [(key, self.repr(value)) for key, value in d.items()]
+ return self.render_object_dump(items, 'Local variables in frame')
+
+ def render_object_dump(self, items, title, repr=None):
+ html_items = []
+ for key, value in items:
+ html_items.append('<tr><th>%s<td><pre class=repr>%s</pre>' %
+ (escape(key), value))
+ if not html_items:
+ html_items.append('<tr><td><em>Nothing</em>')
+ return OBJECT_DUMP_HTML % {
+ 'title': escape(title),
+ 'repr': repr and '<pre class=repr>%s</pre>' % repr or '',
+ 'items': '\n'.join(html_items)
+ }
diff --git a/websdk/werkzeug/debug/shared/FONT_LICENSE b/websdk/werkzeug/debug/shared/FONT_LICENSE
new file mode 100644
index 0000000..ae78a8f
--- /dev/null
+++ b/websdk/werkzeug/debug/shared/FONT_LICENSE
@@ -0,0 +1,96 @@
+-------------------------------
+UBUNTU FONT LICENCE Version 1.0
+-------------------------------
+
+PREAMBLE
+This licence allows the licensed fonts to be used, studied, modified and
+redistributed freely. The fonts, including any derivative works, can be
+bundled, embedded, and redistributed provided the terms of this licence
+are met. The fonts and derivatives, however, cannot be released under
+any other licence. The requirement for fonts to remain under this
+licence does not require any document created using the fonts or their
+derivatives to be published under this licence, as long as the primary
+purpose of the document is not to be a vehicle for the distribution of
+the fonts.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this licence and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Original Version" refers to the collection of Font Software components
+as received under this licence.
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to
+a new environment.
+
+"Copyright Holder(s)" refers to all individuals and companies who have a
+copyright ownership of the Font Software.
+
+"Substantially Changed" refers to Modified Versions which can be easily
+identified as dissimilar to the Font Software by users of the Font
+Software comparing the Original Version with the Modified Version.
+
+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 and with or without charging
+a redistribution fee), making available to the public, and in some
+countries other activities as well.
+
+PERMISSION & CONDITIONS
+This licence does not grant any rights under trademark law and all such
+rights are reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of the Font Software, to propagate the Font Software, subject to
+the below conditions:
+
+1) Each copy of the Font Software must contain the above copyright
+notice and this licence. These can be included either as stand-alone
+text files, human-readable headers or in the appropriate machine-
+readable metadata fields within text or binary files as long as those
+fields can be easily viewed by the user.
+
+2) The font name complies with the following:
+(a) The Original Version must retain its name, unmodified.
+(b) Modified Versions which are Substantially Changed must be renamed to
+avoid use of the name of the Original Version or similar names entirely.
+(c) Modified Versions which are not Substantially Changed must be
+renamed to both (i) retain the name of the Original Version and (ii) add
+additional naming elements to distinguish the Modified Version from the
+Original Version. The name of such Modified Versions must be the name of
+the Original Version, with "derivative X" where X represents the name of
+the new work, appended to that name.
+
+3) The name(s) of the Copyright Holder(s) and any contributor to the
+Font Software shall not be used to promote, endorse or advertise any
+Modified Version, except (i) as required by this licence, (ii) to
+acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
+their explicit written permission.
+
+4) The Font Software, modified or unmodified, in part or in whole, must
+be distributed entirely under this licence, and must not be distributed
+under any other licence. The requirement for fonts to remain under this
+licence does not affect any document created using the Font Software,
+except any version of the Font Software extracted from a document
+created using the Font Software may only be distributed under this
+licence.
+
+TERMINATION
+This licence becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
+DEALINGS IN THE FONT SOFTWARE.
diff --git a/websdk/werkzeug/debug/shared/console.png b/websdk/werkzeug/debug/shared/console.png
new file mode 100755
index 0000000..c28dd63
--- /dev/null
+++ b/websdk/werkzeug/debug/shared/console.png
Binary files differ
diff --git a/websdk/werkzeug/debug/shared/debugger.js b/websdk/werkzeug/debug/shared/debugger.js
new file mode 100644
index 0000000..6468cba
--- /dev/null
+++ b/websdk/werkzeug/debug/shared/debugger.js
@@ -0,0 +1,200 @@
+$(function() {
+ var sourceView = null;
+
+ /**
+ * if we are in console mode, show the console.
+ */
+ if (CONSOLE_MODE && EVALEX) {
+ openShell(null, $('div.console div.inner').empty(), 0);
+ }
+
+ $('div.traceback div.frame').each(function() {
+ var
+ target = $('pre', this)
+ .click(function() {
+ sourceButton.click();
+ }),
+ consoleNode = null, source = null,
+ frameID = this.id.substring(6);
+
+ /**
+ * Add an interactive console to the frames
+ */
+ if (EVALEX)
+ $('<img src="?__debugger__=yes&cmd=resource&f=console.png">')
+ .attr('title', 'Open an interactive python shell in this frame')
+ .click(function() {
+ consoleNode = openShell(consoleNode, target, frameID);
+ return false;
+ })
+ .prependTo(target);
+
+ /**
+ * Show sourcecode
+ */
+ var sourceButton = $('<img src="?__debugger__=yes&cmd=resource&f=source.png">')
+ .attr('title', 'Display the sourcecode for this frame')
+ .click(function() {
+ if (!sourceView)
+ $('h2', sourceView =
+ $('<div class="box"><h2>View Source</h2><div class="sourceview">' +
+ '<table></table></div>')
+ .insertBefore('div.explanation'))
+ .css('cursor', 'pointer')
+ .click(function() {
+ sourceView.slideUp('fast');
+ });
+ $.get(document.location.pathname, {__debugger__: 'yes', cmd:
+ 'source', frm: frameID, s: SECRET}, function(data) {
+ $('table', sourceView)
+ .replaceWith(data);
+ if (!sourceView.is(':visible'))
+ sourceView.slideDown('fast', function() {
+ focusSourceBlock();
+ });
+ else
+ focusSourceBlock();
+ });
+ return false;
+ })
+ .prependTo(target);
+ });
+
+ /**
+ * toggle traceback types on click.
+ */
+ $('h2.traceback').click(function() {
+ $(this).next().slideToggle('fast');
+ $('div.plain').slideToggle('fast');
+ }).css('cursor', 'pointer');
+ $('div.plain').hide();
+
+ /**
+ * Add extra info (this is here so that only users with JavaScript
+ * enabled see it.)
+ */
+ $('span.nojavascript')
+ .removeClass('nojavascript')
+ .html('<p>To switch between the interactive traceback and the plaintext ' +
+ 'one, you can click on the "Traceback" headline. From the text ' +
+ 'traceback you can also create a paste of it. ' + (!EVALEX ? '' :
+ 'For code execution mouse-over the frame you want to debug and ' +
+ 'click on the console icon on the right side.' +
+ '<p>You can execute arbitrary Python code in the stack frames and ' +
+ 'there are some extra helpers available for introspection:' +
+ '<ul><li><code>dump()</code> shows all variables in the frame' +
+ '<li><code>dump(obj)</code> dumps all that\'s known about the object</ul>'));
+
+ /**
+ * Add the pastebin feature
+ */
+ $('div.plain form')
+ .submit(function() {
+ var label = $('input[type="submit"]', this);
+ var old_val = label.val();
+ label.val('submitting...');
+ $.ajax({
+ dataType: 'json',
+ url: document.location.pathname,
+ data: {__debugger__: 'yes', tb: TRACEBACK, cmd: 'paste',
+ s: SECRET},
+ success: function(data) {
+ $('div.plain span.pastemessage')
+ .removeClass('pastemessage')
+ .text('Paste created: ')
+ .append($('<a>#' + data.id + '</a>').attr('href', data.url));
+ },
+ error: function() {
+ alert('Error: Could not submit paste. No network connection?');
+ label.val(old_val);
+ }
+ });
+ return false;
+ });
+
+ // if we have javascript we submit by ajax anyways, so no need for the
+ // not scaling textarea.
+ var plainTraceback = $('div.plain textarea');
+ plainTraceback.replaceWith($('<pre>').text(plainTraceback.text()));
+});
+
+
+/**
+ * Helper function for shell initialization
+ */
+function openShell(consoleNode, target, frameID) {
+ if (consoleNode)
+ return consoleNode.slideToggle('fast');
+ consoleNode = $('<pre class="console">')
+ .appendTo(target.parent())
+ .hide()
+ var historyPos = 0, history = [''];
+ var output = $('<div class="output">[console ready]</div>')
+ .appendTo(consoleNode);
+ var form = $('<form>&gt;&gt;&gt; </form>')
+ .submit(function() {
+ var cmd = command.val();
+ $.get(document.location.pathname, {
+ __debugger__: 'yes', cmd: cmd, frm: frameID, s: SECRET}, function(data) {
+ var tmp = $('<div>').html(data);
+ $('span.extended', tmp).each(function() {
+ var hidden = $(this).wrap('<span>').hide();
+ hidden
+ .parent()
+ .append($('<a href="#" class="toggle">&nbsp;&nbsp;</a>')
+ .click(function() {
+ hidden.toggle();
+ $(this).toggleClass('open')
+ return false;
+ }));
+ });
+ output.append(tmp);
+ command.focus();
+ consoleNode.scrollTop(command.position().top);
+ var old = history.pop();
+ history.push(cmd);
+ if (typeof old != 'undefined')
+ history.push(old);
+ historyPos = history.length - 1;
+ });
+ command.val('');
+ return false;
+ }).
+ appendTo(consoleNode);
+
+ var command = $('<input type="text">')
+ .appendTo(form)
+ .keydown(function(e) {
+ if (e.charCode == 100 && e.ctrlKey) {
+ output.text('--- screen cleared ---');
+ return false;
+ }
+ else if (e.charCode == 0 && (e.keyCode == 38 || e.keyCode == 40)) {
+ if (e.keyCode == 38 && historyPos > 0)
+ historyPos--;
+ else if (e.keyCode == 40 && historyPos < history.length)
+ historyPos++;
+ command.val(history[historyPos]);
+ return false;
+ }
+ });
+
+ return consoleNode.slideDown('fast', function() {
+ command.focus();
+ });
+}
+
+/**
+ * Focus the current block in the source view.
+ */
+function focusSourceBlock() {
+ var tmp, line = $('table.source tr.current');
+ for (var i = 0; i < 7; i++) {
+ tmp = line.prev();
+ if (!(tmp && tmp.is('.in-frame')))
+ break
+ line = tmp;
+ }
+ var container = $('div.sourceview');
+ container.scrollTop(line.offset().top);
+}
diff --git a/websdk/werkzeug/debug/shared/jquery.js b/websdk/werkzeug/debug/shared/jquery.js
new file mode 100644
index 0000000..8f3ca2e
--- /dev/null
+++ b/websdk/werkzeug/debug/shared/jquery.js
@@ -0,0 +1,167 @@
+/*!
+ * jQuery JavaScript Library v1.4.4
+ * 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: Thu Nov 11 19:04:53 2010 -0500
+ */
+(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h=
+h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;k<J.length;k++){h=J[k];h.origType.replace(X,"")===a.type?f.push(h.selector):J.splice(k--,1)}f=c(a.target).closest(f,a.currentTarget);o=0;for(x=f.length;o<x;o++){r=f[o];for(k=0;k<J.length;k++){h=J[k];if(r.selector===h.selector&&(!A||A.test(h.namespace))){l=r.elem;e=null;if(h.preType==="mouseenter"||
+h.preType==="mouseleave"){a.type=h.preType;e=c(a.relatedTarget).closest(h.selector)[0]}if(!e||e!==l)C.push({elem:l,handleObj:h,level:r.level})}}}o=0;for(x=C.length;o<x;o++){f=C[o];if(d&&f.level>d)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La,
+"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,
+e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(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 oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,
+"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+
+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j,
+s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,
+j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length},
+toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j===
+-1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false;
+if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K<Q;K++)if((j=arguments[K])!=null)for(s in j){v=G[s];z=j[s];if(G!==z)if(ga&&z&&(b.isPlainObject(z)||(H=b.isArray(z)))){if(H){H=false;v=v&&b.isArray(v)?v:[]}else v=v&&b.isPlainObject(v)?v:{};G[s]=b.extend(ga,v,z)}else if(z!==B)G[s]=z}return G};b.extend({noConflict:function(j){E.$=e;if(j)E.jQuery=d;return b},isReady:false,readyWait:1,ready:function(j){j===true&&b.readyWait--;
+if(!b.readyWait||j!==true&&!b.isReady){if(!t.body)return setTimeout(b.ready,1);b.isReady=true;if(!(j!==true&&--b.readyWait>0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload",
+b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&&
+!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&&
+l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H<G;){if(s.apply(j[H++],v)===false)break}else if(K)for(z in j){if(s.call(j[z],
+z,j[z])===false)break}else for(v=j[0];H<G&&s.call(v,H,v)!==false;v=j[++H]);return j},trim:O?function(j){return j==null?"":O.call(j)}:function(j){return j==null?"":j.toString().replace(k,"").replace(o,"")},makeArray:function(j,s){var v=s||[];if(j!=null){var z=b.type(j);j.length==null||z==="string"||z==="function"||z==="regexp"||b.isWindow(j)?M.call(v,j):b.merge(v,j)}return v},inArray:function(j,s){if(s.indexOf)return s.indexOf(j);for(var v=0,z=s.length;v<z;v++)if(s[v]===j)return v;return-1},merge:function(j,
+s){var v=j.length,z=0;if(typeof s.length==="number")for(var H=s.length;z<H;z++)j[v++]=s[z];else for(;s[z]!==B;)j[v++]=s[z++];j.length=v;return j},grep:function(j,s,v){var z=[],H;v=!!v;for(var G=0,K=j.length;G<K;G++){H=!!s(j[G],G);v!==H&&z.push(j[G])}return z},map:function(j,s,v){for(var z=[],H,G=0,K=j.length;G<K;G++){H=s(j[G],G,v);if(H!=null)z[z.length]=H}return z.concat.apply([],z)},guid:1,proxy:function(j,s,v){if(arguments.length===2)if(typeof s==="string"){v=j;j=v[s];s=B}else if(s&&!b.isFunction(s)){v=
+s;s=B}if(!s&&j)s=function(){return j.apply(v||this,arguments)};if(j)s.guid=j.guid=j.guid||s.guid||b.guid++;return s},access:function(j,s,v,z,H,G){var K=j.length;if(typeof s==="object"){for(var Q in s)b.access(j,Q,s[Q],z,H,v);return j}if(v!==B){z=!G&&z&&b.isFunction(v);for(Q=0;Q<K;Q++)H(j[Q],s,z?v.call(j[Q],Q,H(j[Q],s)):v,G);return j}return K?H(j[0],s):B},now:function(){return(new Date).getTime()},uaMatch:function(j){j=j.toLowerCase();j=L.exec(j)||g.exec(j)||i.exec(j)||j.indexOf("compatible")<0&&n.exec(j)||
+[];return{browser:j[1]||"",version:j[2]||"0"}},browser:{}});b.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(j,s){R["[object "+s+"]"]=s.toLowerCase()});m=b.uaMatch(m);if(m.browser){b.browser[m.browser]=true;b.browser.version=m.version}if(b.browser.webkit)b.browser.safari=true;if(D)b.inArray=function(j,s){return D.call(s,j)};if(!/\s/.test("\u00a0")){k=/^[\s\xA0]+/;o=/[\s\xA0]+$/}f=b(t);if(t.addEventListener)u=function(){t.removeEventListener("DOMContentLoaded",u,
+false);b.ready()};else if(t.attachEvent)u=function(){if(t.readyState==="complete"){t.detachEvent("onreadystatechange",u);b.ready()}};return E.jQuery=E.$=b}();(function(){c.support={};var a=t.documentElement,b=t.createElement("script"),d=t.createElement("div"),e="script"+c.now();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 f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"),
+k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false,
+scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom=
+1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="<div style='width:4px;'></div>";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="<table><tr><td style='padding:0;display:none'></td><td>t</td></tr></table>";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display=
+"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h=
+c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);
+else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h<l;h++){f=e[h].name;if(f.indexOf("data-")===0){f=f.substr(5);ka(this[0],f,d[f])}}}return d}else if(typeof a==="object")return this.each(function(){c.data(this,
+a)});var k=a.split(".");k[1]=k[1]?"."+k[1]:"";if(b===B){d=this.triggerHandler("getData"+k[1]+"!",[k[0]]);if(d===B&&this.length){d=c.data(this[0],a);d=ka(this[0],a,d)}return d===B&&k[1]?this.data(k[0]):d}else return this.each(function(){var o=c(this),x=[k[0],b];o.triggerHandler("setData"+k[1]+"!",x);c.data(this,a,b);o.triggerHandler("changeData"+k[1]+"!",x)})},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 e=
+c.data(a,b);if(!d)return e||[];if(!e||c.isArray(d))e=c.data(a,b,c.makeArray(d));else e.push(d);return e}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),e=d.shift();if(e==="inprogress")e=d.shift();if(e){b==="fx"&&d.unshift("inprogress");e.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===B)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 sa=/[\n\t]/g,ha=/\s+/,Sa=/\r/g,Ta=/^(?:href|src|style)$/,Ua=/^(?:button|input)$/i,Va=/^(?:button|input|object|select|textarea)$/i,Wa=/^a(?:rea)?$/i,ta=/^(?:radio|checkbox)$/i;c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",
+colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};c.fn.extend({attr:function(a,b){return c.access(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(x){var r=c(this);r.addClass(a.call(this,x,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===
+1)if(f.className){for(var h=" "+f.className+" ",l=f.className,k=0,o=b.length;k<o;k++)if(h.indexOf(" "+b[k]+" ")<0)l+=" "+b[k];f.className=c.trim(l)}else f.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var x=c(this);x.removeClass(a.call(this,o,x.attr("class")))});if(a&&typeof a==="string"||a===B)for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===1&&f.className)if(a){for(var h=(" "+f.className+" ").replace(sa," "),
+l=0,k=b.length;l<k;l++)h=h.replace(" "+b[l]+" "," ");f.className=c.trim(h)}else f.className=""}return this},toggleClass:function(a,b){var d=typeof a,e=typeof b==="boolean";if(c.isFunction(a))return this.each(function(f){var h=c(this);h.toggleClass(a.call(this,f,h.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var f,h=0,l=c(this),k=b,o=a.split(ha);f=o[h++];){k=e?k:!l.hasClass(f);l[k?"addClass":"removeClass"](f)}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(sa," ").indexOf(a)>-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one";
+if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h<e;h++){var l=f[h];if(l.selected&&(c.support.optDisabled?!l.disabled:l.getAttribute("disabled")===null)&&(!l.parentNode.disabled||!c.nodeName(l.parentNode,"optgroup"))){a=c(l).val();if(b)return a;d.push(a)}}return d}if(ta.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Sa,"")}return B}var k=c.isFunction(a);return this.each(function(o){var x=c(this),r=a;if(this.nodeType===1){if(k)r=
+a.call(this,o,x.val());if(r==null)r="";else if(typeof r==="number")r+="";else if(c.isArray(r))r=c.map(r,function(C){return C==null?"":C+""});if(c.isArray(r)&&ta.test(this.type))this.checked=c.inArray(x.val(),r)>=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.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,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else 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:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0};
+c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,
+arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid=
+d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+
+c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h<A.length;h++){C=A[h];if(d.guid===C.guid){if(k||x.test(C.namespace)){e==null&&A.splice(h--,1);r.remove&&r.remove.call(a,C)}if(e!=null)break}}if(A.length===0||e!=null&&A.length===1){if(!r.teardown||r.teardown.call(a,o)===false)c.removeEvent(a,f,w.handle);delete I[f]}}else for(h=0;h<A.length;h++){C=A[h];if(k||x.test(C.namespace)){c.event.remove(a,r,C.handler,h);A.splice(h--,1)}}}if(c.isEmptyObject(I)){if(b=
+w.handle)b.elem=null;delete w.events;delete w.handle;if(typeof w==="function")c.removeData(a,J);else c.isEmptyObject(w)&&c.removeData(a)}}}}},trigger:function(a,b,d,e){var f=a.type||a;if(!e){a=typeof a==="object"?a[c.expando]?a:c.extend(c.Event(f),a):c.Event(f);if(f.indexOf("!")>=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===
+8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k===
+"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+
+d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f<l;f++){var k=d[f];if(b||e.test(k.namespace)){a.handler=k.handler;a.data=k.data;a.handleObj=k;k=k.handler.apply(this,h);if(k!==B){a.result=k;if(k===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 pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[c.expando])return a;var b=a;a=c.Event(b);for(var d=this.props.length,e;d;){e=this.props[--d];a[e]=b[e]}if(!a.target)a.target=a.srcElement||t;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=t.documentElement;d=t.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==null&&(a.charCode!=null||a.keyCode!=null))a.which=a.charCode!=null?a.charCode:a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==B)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,Y(a.origType,a.selector),c.extend({},a,{handler:Ka,guid:a.handler.guid}))},remove:function(a){c.event.remove(this,
+Y(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,d){if(c.isWindow(this))this.onbeforeunload=d},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.removeEvent=t.removeEventListener?function(a,b,d){a.removeEventListener&&a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent&&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=
+c.now();this[c.expando]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=ca;var a=this.originalEvent;if(a)if(a.preventDefault)a.preventDefault();else a.returnValue=false},stopPropagation:function(){this.isPropagationStopped=ca;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=ca;this.stopPropagation()},isDefaultPrevented:U,isPropagationStopped:U,isImmediatePropagationStopped:U};
+var va=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){}},wa=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?wa:va,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?wa:va)}}});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){a.liveFired=B;return la("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){a.liveFired=B;return la("submit",this,arguments)}})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};if(!c.support.changeBubbles){var V,
+xa=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(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired=
+B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.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 Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type===
+"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]===
+0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h<k;h++)c.event.add(this[h],d,l,e)}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 e=this.length;d<e;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,e){return this.live(b,d,e,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]){var d=c.Event(a);d.preventDefault();d.stopPropagation();c.event.trigger(d,b,this[0]);return d.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(e){var f=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,f+1);e.preventDefault();return b[f].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var ya={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,e,f,h){var l,k=0,o,x,r=h||this.selector;h=h?this:c(this.context);if(typeof d===
+"object"&&!d.preventDefault){for(l in d)h[b](l,e,d[l],r);return this}if(c.isFunction(e)){f=e;e=B}for(d=(d||"").split(" ");(l=d[k++])!=null;){o=X.exec(l);x="";if(o){x=o[0];l=l.replace(X,"")}if(l==="hover")d.push("mouseenter"+x,"mouseleave"+x);else{o=l;if(l==="focus"||l==="blur"){d.push(ya[l]+x);l+=x}else l=(ya[l]||l)+x;if(b==="live"){x=0;for(var A=h.length;x<A;x++)c.event.add(h[x],"live."+Y(l,r),{data:e,selector:r,handler:f,origType:l,origHandler:f,preType:o})}else h.unbind("live."+Y(l,r),f)}}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,e){if(e==null){e=d;d=null}return arguments.length>0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",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,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1&&!q){y.sizcache=n;y.sizset=p}if(y.nodeName.toLowerCase()===i){F=y;break}y=y[g]}m[p]=F}}}function b(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1){if(!q){y.sizcache=n;y.sizset=p}if(typeof i!=="string"){if(y===i){F=true;break}}else if(k.filter(i,
+[y]).length>0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3];
+break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr,
+q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h=
+l;g.sort(w);if(h)for(var i=1;i<g.length;i++)g[i]===g[i-1]&&g.splice(i--,1)}return g};k.matches=function(g,i){return k(g,null,null,i)};k.matchesSelector=function(g,i){return k(i,null,null,[g]).length>0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p<q;p++){var u,y=o.order[p];if(u=o.leftMatch[y].exec(g)){var F=u[1];u.splice(1,1);if(F.substr(F.length-1)!=="\\"){u[1]=(u[1]||"").replace(/\\/g,"");m=o.find[y](u,i,n);if(m!=null){g=g.replace(o.match[y],"");break}}}}m||(m=i.getElementsByTagName("*"));
+return{set:m,expr:g}};k.filter=function(g,i,n,m){for(var p,q,u=g,y=[],F=i,M=i&&i[0]&&k.isXML(i[0]);g&&i.length;){for(var N in o.filter)if((p=o.leftMatch[N].exec(g))!=null&&p[2]){var O,D,R=o.filter[N];D=p[1];q=false;p.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(F===y)y=[];if(o.preFilter[N])if(p=o.preFilter[N](p,F,n,y,m,M)){if(p===true)continue}else q=O=true;if(p)for(var j=0;(D=F[j])!=null;j++)if(D){O=R(D,p,j,F);var s=m^!!O;if(n&&O!=null)if(s)q=true;else F[j]=false;else if(s){y.push(D);q=true}}if(O!==
+B){n||(F=y);g=g.replace(o.match[N],"");if(!q)return[];break}}}if(g===u)if(q==null)k.error(g);else break;u=g}return F};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var o=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,i){var n=typeof i==="string",m=n&&!/\W/.test(i);n=n&&!m;if(m)i=i.toLowerCase();m=0;for(var p=g.length,q;m<p;m++)if(q=g[m]){for(;(q=q.previousSibling)&&q.nodeType!==1;);g[m]=n||q&&q.nodeName.toLowerCase()===
+i?q||false:q===i}n&&k.filter(i,g,true)},">":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p<q;p++){if(n=g[p]){n=n.parentNode;g[p]=n.nodeName.toLowerCase()===i?n:false}}else{for(;p<q;p++)if(n=g[p])g[p]=m?n.parentNode:n.parentNode===i;m&&k.filter(i,g,true)}},"":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=i=i.toLowerCase();q=a}q("parentNode",i,p,g,m,n)},"~":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=
+i=i.toLowerCase();q=a}q("previousSibling",i,p,g,m,n)}},find:{ID:function(g,i,n){if(typeof i.getElementById!=="undefined"&&!n)return(g=i.getElementById(g[1]))&&g.parentNode?[g]:[]},NAME:function(g,i){if(typeof i.getElementsByName!=="undefined"){for(var n=[],m=i.getElementsByName(g[1]),p=0,q=m.length;p<q;p++)m[p].getAttribute("name")===g[1]&&n.push(m[p]);return n.length===0?null:n}},TAG:function(g,i){return i.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,i,n,m,p,q){g=" "+g[1].replace(/\\/g,
+"")+" ";if(q)return g;q=0;for(var u;(u=i[q])!=null;q++)if(u)if(p^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))n||m.push(u);else if(n)i[q]=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 i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n,
+m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.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,i,n){return!!k(n[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,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return i<n[3]-0},gt:function(g,i,n){return i>n[3]-0},nth:function(g,i,n){return n[3]-
+0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n<m;n++)if(i[n]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+p)},CHILD:function(g,i){var n=i[1],m=g;switch(n){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(n===
+"first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":n=i[2];var p=i[3];if(n===1&&p===0)return true;var q=i[0],u=g.parentNode;if(u&&(u.sizcache!==q||!g.nodeIndex)){var y=0;for(m=u.firstChild;m;m=m.nextSibling)if(m.nodeType===1)m.nodeIndex=++y;u.sizcache=q}m=g.nodeIndex-p;return n===0?m===0:m%n===0&&m/n>=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===
+i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]];
+if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m,
+g);else if(typeof g.length==="number")for(var p=g.length;n<p;n++)m.push(g[n]);else for(;g[n];n++)m.push(g[n]);return m}}var w,I;if(t.documentElement.compareDocumentPosition)w=function(g,i){if(g===i){h=true;return 0}if(!g.compareDocumentPosition||!i.compareDocumentPosition)return g.compareDocumentPosition?-1:1;return g.compareDocumentPosition(i)&4?-1:1};else{w=function(g,i){var n,m,p=[],q=[];n=g.parentNode;m=i.parentNode;var u=n;if(g===i){h=true;return 0}else if(n===m)return I(g,i);else if(n){if(!m)return 1}else return-1;
+for(;u;){p.unshift(u);u=u.parentNode}for(u=m;u;){q.unshift(u);u=u.parentNode}n=p.length;m=q.length;for(u=0;u<n&&u<m;u++)if(p[u]!==q[u])return I(p[u],q[u]);return u===n?I(g,q[u],-1):I(p[u],i,1)};I=function(g,i,n){if(g===i)return n;for(g=g.nextSibling;g;){if(g===i)return-1;g=g.nextSibling}return 1}}k.getText=function(g){for(var i="",n,m=0;g[m];m++){n=g[m];if(n.nodeType===3||n.nodeType===4)i+=n.nodeValue;else if(n.nodeType!==8)i+=k.getText(n.childNodes)}return i};(function(){var g=t.createElement("div"),
+i="script"+(new Date).getTime(),n=t.documentElement;g.innerHTML="<a name='"+i+"'/>";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g);
+n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&&
+function(){var g=k,i=t.createElement("div");i.innerHTML="<p class='TEST'></p>";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F||
+p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g=
+t.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){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition?
+function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n<u;n++)k(g,q[n],m);return k.filter(p,m)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=k.getText;c.isXMLDoc=k.isXML;
+c.contains=k.contains})();var Za=/Until$/,$a=/^(?:parents|prevUntil|prevAll)/,ab=/,/,Na=/^.[^:#\[\.,]*$/,bb=Array.prototype.slice,cb=c.expr.match.POS;c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,e=0,f=this.length;e<f;e++){d=b.length;c.find(a,this[e],b);if(e>0)for(var h=d;h<b.length;h++)for(var l=0;l<d;l++)if(b[l]===b[h]){b.splice(h--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,e=b.length;d<e;d++)if(c.contains(this,b[d]))return true})},
+not:function(a){return this.pushStack(ma(this,a,false),"not",a)},filter:function(a){return this.pushStack(ma(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e<f;e++){l=a[e];k[l]||(k[l]=c.expr.match.POS.test(l)?c(l,b||this.context):l)}for(;h&&h.ownerDocument&&h!==b;){for(l in k){e=k[l];if(e.jquery?e.index(h)>-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h=
+h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e<f;e++)for(h=this[e];h;)if(l?l.index(h)>-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},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){var d=typeof a==="string"?c(a,b||this.context):
+c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},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,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&
+e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===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 za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/<tbody/i,eb=/<|&#?\w+;/,Ca=/<(?:script|object|embed|option|style)/i,Da=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/\=([^="'>\s]+\/)>/g,P={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,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._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!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).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,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}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,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null;
+else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1></$2>");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(e){this.empty().append(a)}}else c.isFunction(a)?this.each(function(f){var h=c(this);h.html(a.call(this,f,h.html()))}):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),e=d.html();d.replaceWith(a.call(this,b,e))});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){var e,f,h,l=a[0],k=[];if(!c.support.checkClone&&arguments.length===3&&typeof l==="string"&&Da.test(l))return this.each(function(){c(this).domManip(a,
+b,d,true)});if(c.isFunction(l))return this.each(function(x){var r=c(this);a[0]=l.call(this,x,b?r.html():B);r.domManip(a,b,d)});if(this[0]){e=l&&l.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:c.buildFragment(a,this,k);h=e.fragment;if(f=h.childNodes.length===1?h=h.firstChild:h.firstChild){b=b&&c.nodeName(f,"tr");f=0;for(var o=this.length;f<o;f++)d.call(b?c.nodeName(this[f],"table")?this[f].getElementsByTagName("tbody")[0]||this[f].appendChild(this[f].ownerDocument.createElement("tbody")):
+this[f]:this[f],f>0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append",
+prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f<h;f++){var l=(f>0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument||
+b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1></$2>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]==="<table>"&&!x?r.childNodes:[];for(o=k.length-
+1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));
+d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i,
+jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,
+zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),
+h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b);
+if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=
+d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left;
+e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b===
+"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("<div>").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});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||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).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,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})},
+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,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},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:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html",
+script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data||
+!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache=
+false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset;
+A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type",
+b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&&
+c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d||
+c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]=
+encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess",
+[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),
+e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}});
+if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show",
+3),a,b,d);else{d=0;for(var e=this.length;d<e;d++){a=this[d];b=a.style.display;if(!c.data(a,"olddisplay")&&b==="none")b=a.style.display="";b===""&&c.css(a,"display")==="none"&&c.data(a,"olddisplay",qa(a.nodeName))}for(d=0;d<e;d++){a=this[d];b=a.style.display;if(b===""||b==="none")a.style.display=c.data(a,"olddisplay")||""}return this}},hide:function(a,b,d){if(a||a===0)return this.animate(S("hide",3),a,b,d);else{a=0;for(b=this.length;a<b;a++){d=c.css(this[a],"display");d!=="none"&&c.data(this[a],"olddisplay",
+d)}for(a=0;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b,d){var e=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||e?this.each(function(){var f=e?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(S("toggle",3),a,b,d);return this},fadeTo:function(a,b,d,e){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d,e)},animate:function(a,b,d,e){var f=c.speed(b,
+d,e);if(c.isEmptyObject(a))return this.each(f.complete);return this[f.queue===false?"each":"queue"](function(){var h=c.extend({},f),l,k=this.nodeType===1,o=k&&c(this).is(":hidden"),x=this;for(l in a){var r=c.camelCase(l);if(l!==r){a[r]=a[l];delete a[l];l=r}if(a[l]==="hide"&&o||a[l]==="show"&&!o)return h.complete.call(this);if(k&&(l==="height"||l==="width")){h.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(c.css(this,"display")==="inline"&&c.css(this,"float")==="none")if(c.support.inlineBlockNeedsLayout)if(qa(this.nodeName)===
+"inline")this.style.display="inline-block";else{this.style.display="inline";this.style.zoom=1}else this.style.display="inline-block"}if(c.isArray(a[l])){(h.specialEasing=h.specialEasing||{})[l]=a[l][1];a[l]=a[l][0]}}if(h.overflow!=null)this.style.overflow="hidden";h.curAnim=c.extend({},a);c.each(a,function(A,C){var J=new c.fx(x,h,A);if(vb.test(C))J[C==="toggle"?o?"show":"hide":C](a);else{var w=wb.exec(C),I=J.cur()||0;if(w){var L=parseFloat(w[2]),g=w[3]||"px";if(g!=="px"){c.style(x,A,(L||1)+g);I=(L||
+1)/J.cur()*I;c.style(x,A,I+g)}if(w[1])L=(w[1]==="-="?-1:1)*L+I;J.custom(I,L,g)}else J.custom(I,C,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var e=d.length-1;e>=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b,
+d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a*
+Math.PI)/2+0.5)*e+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)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)}
+var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},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=c.now(),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 e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide||
+this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,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()},interval:13,stop:function(){clearInterval(ba);ba=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};var xb=/^t(?:able|d|h)$/i,Ia=/^(?:body|html)$/i;c.fn.offset="getBoundingClientRect"in t.documentElement?function(a){var b=this[0],d;if(a)return this.each(function(l){c.offset.setOffset(this,a,l)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);try{d=b.getBoundingClientRect()}catch(e){}var f=b.ownerDocument,h=f.documentElement;if(!d||!c.contains(h,b))return d||{top:0,left:0};b=f.body;f=fa(f);return{top:d.top+(f.pageYOffset||c.support.boxModel&&
+h.scrollTop||b.scrollTop)-(h.clientTop||b.clientTop||0),left:d.left+(f.pageXOffset||c.support.boxModel&&h.scrollLeft||b.scrollLeft)-(h.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(x){c.offset.setOffset(this,a,x)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d,e=b.offsetParent,f=b.ownerDocument,h=f.documentElement,l=f.body;d=(f=f.defaultView)?f.getComputedStyle(b,null):b.currentStyle;
+for(var k=b.offsetTop,o=b.offsetLeft;(b=b.parentNode)&&b!==l&&b!==h;){if(c.offset.supportsFixedPosition&&d.position==="fixed")break;d=f?f.getComputedStyle(b,null):b.currentStyle;k-=b.scrollTop;o-=b.scrollLeft;if(b===e){k+=b.offsetTop;o+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&xb.test(b.nodeName))){k+=parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}e=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"){k+=
+parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}d=d}if(d.position==="relative"||d.position==="static"){k+=l.offsetTop;o+=l.offsetLeft}if(c.offset.supportsFixedPosition&&d.position==="fixed"){k+=Math.max(h.scrollTop,l.scrollTop);o+=Math.max(h.scrollLeft,l.scrollLeft)}return{top:k,left:o}};c.offset={initialize:function(){var a=t.body,b=t.createElement("div"),d,e,f,h=parseFloat(c.css(a,"marginTop"))||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;e=d.firstChild;f=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=e.offsetTop!==5;this.doesAddBorderForTableAndCells=
+f.offsetTop===5;e.style.position="fixed";e.style.top="20px";this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15;e.style.position=e.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==h;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.css(a,
+"marginTop"))||0;d+=parseFloat(c.css(a,"marginLeft"))||0}return{top:b,left:d}},setOffset:function(a,b,d){var e=c.css(a,"position");if(e==="static")a.style.position="relative";var f=c(a),h=f.offset(),l=c.css(a,"top"),k=c.css(a,"left"),o=e==="absolute"&&c.inArray("auto",[l,k])>-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a,
+e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.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(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();
+c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+
+b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window);
diff --git a/websdk/werkzeug/debug/shared/less.png b/websdk/werkzeug/debug/shared/less.png
new file mode 100755
index 0000000..5efefd6
--- /dev/null
+++ b/websdk/werkzeug/debug/shared/less.png
Binary files differ
diff --git a/websdk/werkzeug/debug/shared/more.png b/websdk/werkzeug/debug/shared/more.png
new file mode 100755
index 0000000..804fa22
--- /dev/null
+++ b/websdk/werkzeug/debug/shared/more.png
Binary files differ
diff --git a/websdk/werkzeug/debug/shared/source.png b/websdk/werkzeug/debug/shared/source.png
new file mode 100755
index 0000000..f7ea904
--- /dev/null
+++ b/websdk/werkzeug/debug/shared/source.png
Binary files differ
diff --git a/websdk/werkzeug/debug/shared/style.css b/websdk/werkzeug/debug/shared/style.css
new file mode 100644
index 0000000..53a107e
--- /dev/null
+++ b/websdk/werkzeug/debug/shared/style.css
@@ -0,0 +1,113 @@
+@font-face {
+ font-family: 'Ubuntu';
+ font-style: normal;
+ font-weight: normal;
+ src: local('Ubuntu'), local('Ubuntu-Regular'),
+ url('?__debugger__=yes&cmd=resource&f=ubuntu.ttf') format('truetype');
+}
+
+body, input { font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+ 'Verdana', sans-serif; color: #000; text-align: center;
+ margin: 1em; padding: 0; font-size: 15px; }
+h1, h2, h3 { font-family: 'Ubuntu', 'Lucida Grande', 'Lucida Sans Unicode',
+ 'Geneva', 'Verdana', sans-serif; font-weight: normal; }
+
+input { background-color: #fff; margin: 0; text-align: left;
+ outline: none !important; }
+a { color: #11557C; }
+a:hover { color: #177199; }
+pre, code, table.source,
+textarea { font-family: 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono',
+ monospace; font-size: 14px; }
+
+div.debugger { text-align: left; padding: 12px; margin: auto;
+ background-color: white; }
+h1 { font-size: 36px; margin: 0 0 0.3em 0; }
+div.detail p { margin: 0 0 8px 13px; font-size: 14px; white-space: pre-wrap; }
+div.explanation { margin: 20px 13px; font-size: 15px; color: #555; }
+div.footer { font-size: 13px; text-align: right; margin: 30px 0;
+ color: #86989B; }
+
+h2 { font-size: 16px; margin: 1.3em 0 0.0 0; padding: 9px;
+ background-color: #11557C; color: white; }
+h2 em, h3 em { font-style: normal; color: #A5D6D9; font-weight: normal; }
+
+div.traceback, div.plain { border: 1px solid #ddd; margin: 0 0 1em 0; padding: 10px; }
+div.plain p { margin: 0; }
+div.plain textarea,
+div.plain pre { margin: 10px 0 0 0; padding: 4px;
+ background-color: #E8EFF0; border: 1px solid #D3E7E9; }
+div.plain textarea { width: 99%; height: 300px; }
+div.traceback h3 { font-size: 1em; margin: 0 0 0.8em 0; }
+div.traceback ul { list-style: none; margin: 0; padding: 0 0 0 1em; }
+div.traceback h4 { font-size: 13px; font-weight: normal; margin: 0.7em 0 0.1em 0; }
+div.traceback pre { margin: 0; padding: 5px 0 3px 15px;
+ background-color: #E8EFF0; border: 1px solid #D3E7E9; }
+div.traceback pre,
+div.box table.source { white-space: pre-wrap; /* css-3 should we be so lucky... */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 ?? */
+ white-space: -o-pre-wrap; /* Opera 7 ?? */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ _white-space: pre; /* IE only hack to re-specify in
+ addition to word-wrap */ }
+div.traceback pre:hover { background-color: #DDECEE; color: black; cursor: pointer; }
+div.traceback blockquote { margin: 1em 0 0 0; padding: 0; }
+div.traceback img { float: right; padding: 2px; margin: -3px 2px 0 0; display: none; }
+div.traceback img:hover { background-color: #ddd; cursor: pointer;
+ border-color: #BFDDE0; }
+div.traceback pre:hover img { display: block; }
+div.traceback cite.filename { font-style: normal; color: #3B666B; }
+
+pre.console { border: 1px solid #ccc; background: white!important;
+ color: black; padding: 5px!important;
+ margin: 3px 0 0 0!important; cursor: default!important;
+ max-height: 400px; overflow: auto; }
+pre.console form { color: #555; }
+pre.console input { background-color: transparent; color: #555;
+ width: 90%; font-family: 'Consolas', 'Deja Vu Sans Mono',
+ 'Bitstream Vera Sans Mono', monospace; font-size: 14px;
+ border: none!important; }
+
+span.string { color: #30799B; }
+span.number { color: #9C1A1C; }
+span.help { color: #3A7734; }
+span.object { color: #485F6E; }
+span.extended { opacity: 0.5; }
+span.extended:hover { opacity: 1; }
+a.toggle { text-decoration: none; background-repeat: no-repeat;
+ background-position: center center;
+ background-image: url(?__debugger__=yes&cmd=resource&f=more.png); }
+a.toggle:hover { background-color: #444; }
+a.open { background-image: url(?__debugger__=yes&cmd=resource&f=less.png); }
+
+pre.console div.traceback,
+pre.console div.box { margin: 5px 10px; white-space: normal;
+ border: 1px solid #11557C; padding: 10px;
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+ 'Verdana', sans-serif; }
+pre.console div.box h3,
+pre.console div.traceback h3 { margin: -10px -10px 10px -10px; padding: 5px;
+ background: #11557C; color: white; }
+
+pre.console div.traceback pre:hover { cursor: default; background: #E8EFF0; }
+pre.console div.traceback pre.syntaxerror { background: inherit; border: none;
+ margin: 20px -10px -10px -10px;
+ padding: 10px; border-top: 1px solid #BFDDE0;
+ background: #E8EFF0; }
+pre.console div.noframe-traceback pre.syntaxerror { margin-top: -10px; border: none; }
+
+pre.console div.box pre.repr { padding: 0; margin: 0; background-color: white; border: none; }
+pre.console div.box table { margin-top: 6px; }
+pre.console div.box pre { border: none; }
+pre.console div.box pre.help { background-color: white; }
+pre.console div.box pre.help:hover { cursor: default; }
+pre.console table tr { vertical-align: top; }
+div.console { border: 1px solid #ccc; padding: 4px; background-color: #fafafa; }
+
+div.box table.source { border-collapse: collapse; width: 100%; background: #E8EFF0 }
+div.box table.source td { border-top: 1px solid #E8EFF0; padding: 4px 0 4px 10px; }
+div.box table.source td.lineno { color: #999; padding-right: 10px; width: 1px; }
+div.box table.source tr.in-frame { background-color: white; }
+div.box table.source tr.current { background-color: #EEF7F8; color: #23707E; }
+div.sourceview { max-height: 400px; overflow: auto; border: 1px solid #ccc; }
diff --git a/websdk/werkzeug/debug/shared/ubuntu.ttf b/websdk/werkzeug/debug/shared/ubuntu.ttf
new file mode 100644
index 0000000..8079f93
--- /dev/null
+++ b/websdk/werkzeug/debug/shared/ubuntu.ttf
Binary files differ
diff --git a/websdk/werkzeug/debug/tbtools.py b/websdk/werkzeug/debug/tbtools.py
new file mode 100644
index 0000000..4bcba21
--- /dev/null
+++ b/websdk/werkzeug/debug/tbtools.py
@@ -0,0 +1,483 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.debug.tbtools
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ This module provides various traceback related utility functions.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD.
+"""
+import re
+import os
+import sys
+import inspect
+import traceback
+import codecs
+from tokenize import TokenError
+from werkzeug.utils import cached_property, escape
+from werkzeug.debug.console import Console
+
+_coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
+_line_re = re.compile(r'^(.*?)$(?m)')
+_funcdef_re = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
+UTF8_COOKIE = '\xef\xbb\xbf'
+
+system_exceptions = (SystemExit, KeyboardInterrupt)
+try:
+ system_exceptions += (GeneratorExit,)
+except NameError:
+ pass
+
+
+HEADER = u'''\
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+ <title>%(title)s // Werkzeug Debugger</title>
+ <link rel="stylesheet" href="?__debugger__=yes&amp;cmd=resource&amp;f=style.css" type="text/css">
+ <script type="text/javascript" src="?__debugger__=yes&amp;cmd=resource&amp;f=jquery.js"></script>
+ <script type="text/javascript" src="?__debugger__=yes&amp;cmd=resource&amp;f=debugger.js"></script>
+ <script type="text/javascript">
+ var TRACEBACK = %(traceback_id)d,
+ CONSOLE_MODE = %(console)s,
+ EVALEX = %(evalex)s,
+ SECRET = "%(secret)s";
+ </script>
+ </head>
+ <body>
+ <div class="debugger">
+'''
+FOOTER = u'''\
+ <div class="footer">
+ Brought to you by <strong class="arthur">DON'T PANIC</strong>, your
+ friendly Werkzeug powered traceback interpreter.
+ </div>
+ </div>
+ </body>
+</html>
+'''
+
+PAGE_HTML = HEADER + u'''\
+<h1>%(exception_type)s</h1>
+<div class="detail">
+ <p class="errormsg">%(exception)s</p>
+</div>
+<h2 class="traceback">Traceback <em>(most recent call last)</em></h2>
+%(summary)s
+<div class="plain">
+ <form action="%(lodgeit_url)s" method="post">
+ <p>
+ <input type="hidden" name="language" value="pytb">
+ This is the Copy/Paste friendly version of the traceback. <span
+ class="pastemessage">You can also paste this traceback into LodgeIt:
+ <input type="submit" value="create paste"></span>
+ </p>
+ <textarea cols="50" rows="10" name="code" readonly>%(plaintext)s</textarea>
+ </form>
+</div>
+<div class="explanation">
+ The debugger caught an exception in your WSGI application. You can now
+ look at the traceback which led to the error. <span class="nojavascript">
+ If you enable JavaScript you can also use additional features such as code
+ execution (if the evalex feature is enabled), automatic pasting of the
+ exceptions and much more.</span>
+</div>
+''' + FOOTER + '''
+<!--
+
+%(plaintext_cs)s
+
+-->
+'''
+
+CONSOLE_HTML = HEADER + u'''\
+<h1>Interactive Console</h1>
+<div class="explanation">
+In this console you can execute Python expressions in the context of the
+application. The initial namespace was created by the debugger automatically.
+</div>
+<div class="console"><div class="inner">The Console requires JavaScript.</div></div>
+''' + FOOTER
+
+SUMMARY_HTML = u'''\
+<div class="%(classes)s">
+ %(title)s
+ <ul>%(frames)s</ul>
+ %(description)s
+</div>
+'''
+
+FRAME_HTML = u'''\
+<div class="frame" id="frame-%(id)d">
+ <h4>File <cite class="filename">"%(filename)s"</cite>,
+ line <em class="line">%(lineno)s</em>,
+ in <code class="function">%(function_name)s</code></h4>
+ <pre>%(current_line)s</pre>
+</div>
+'''
+
+SOURCE_TABLE_HTML = u'<table class=source>%s</table>'
+
+SOURCE_LINE_HTML = u'''\
+<tr class="%(classes)s">
+ <td class=lineno>%(lineno)s</td>
+ <td>%(code)s</td>
+</tr>
+'''
+
+
+def render_console_html(secret):
+ return CONSOLE_HTML % {
+ 'evalex': 'true',
+ 'console': 'true',
+ 'title': 'Console',
+ 'secret': secret,
+ 'traceback_id': -1
+ }
+
+
+def get_current_traceback(ignore_system_exceptions=False,
+ show_hidden_frames=False, skip=0):
+ """Get the current exception info as `Traceback` object. Per default
+ calling this method will reraise system exceptions such as generator exit,
+ system exit or others. This behavior can be disabled by passing `False`
+ to the function as first parameter.
+ """
+ exc_type, exc_value, tb = sys.exc_info()
+ if ignore_system_exceptions and exc_type in system_exceptions:
+ raise
+ for x in xrange(skip):
+ if tb.tb_next is None:
+ break
+ tb = tb.tb_next
+ tb = Traceback(exc_type, exc_value, tb)
+ if not show_hidden_frames:
+ tb.filter_hidden_frames()
+ return tb
+
+
+class Line(object):
+ """Helper for the source renderer."""
+ __slots__ = ('lineno', 'code', 'in_frame', 'current')
+
+ def __init__(self, lineno, code):
+ self.lineno = lineno
+ self.code = code
+ self.in_frame = False
+ self.current = False
+
+ def classes(self):
+ rv = ['line']
+ if self.in_frame:
+ rv.append('in-frame')
+ if self.current:
+ rv.append('current')
+ return rv
+ classes = property(classes)
+
+ def render(self):
+ return SOURCE_LINE_HTML % {
+ 'classes': u' '.join(self.classes),
+ 'lineno': self.lineno,
+ 'code': escape(self.code)
+ }
+
+
+class Traceback(object):
+ """Wraps a traceback."""
+
+ def __init__(self, exc_type, exc_value, tb):
+ self.exc_type = exc_type
+ self.exc_value = exc_value
+ if not isinstance(exc_type, str):
+ exception_type = exc_type.__name__
+ if exc_type.__module__ not in ('__builtin__', 'exceptions'):
+ exception_type = exc_type.__module__ + '.' + exception_type
+ else:
+ exception_type = exc_type
+ self.exception_type = exception_type
+
+ # we only add frames to the list that are not hidden. This follows
+ # the the magic variables as defined by paste.exceptions.collector
+ self.frames = []
+ while tb:
+ self.frames.append(Frame(exc_type, exc_value, tb))
+ tb = tb.tb_next
+
+ def filter_hidden_frames(self):
+ """Remove the frames according to the paste spec."""
+ if not self.frames:
+ return
+
+ new_frames = []
+ hidden = False
+ for frame in self.frames:
+ hide = frame.hide
+ if hide in ('before', 'before_and_this'):
+ new_frames = []
+ hidden = False
+ if hide == 'before_and_this':
+ continue
+ elif hide in ('reset', 'reset_and_this'):
+ hidden = False
+ if hide == 'reset_and_this':
+ continue
+ elif hide in ('after', 'after_and_this'):
+ hidden = True
+ if hide == 'after_and_this':
+ continue
+ elif hide or hidden:
+ continue
+ new_frames.append(frame)
+
+ # if we only have one frame and that frame is from the codeop
+ # module, remove it.
+ if len(new_frames) == 1 and self.frames[0].module == 'codeop':
+ del self.frames[:]
+
+ # if the last frame is missing something went terrible wrong :(
+ elif self.frames[-1] in new_frames:
+ self.frames[:] = new_frames
+
+ def is_syntax_error(self):
+ """Is it a syntax error?"""
+ return isinstance(self.exc_value, SyntaxError)
+ is_syntax_error = property(is_syntax_error)
+
+ def exception(self):
+ """String representation of the exception."""
+ buf = traceback.format_exception_only(self.exc_type, self.exc_value)
+ return ''.join(buf).strip().decode('utf-8', 'replace')
+ exception = property(exception)
+
+ def log(self, logfile=None):
+ """Log the ASCII traceback into a file object."""
+ if logfile is None:
+ logfile = sys.stderr
+ tb = self.plaintext.encode('utf-8', 'replace').rstrip() + '\n'
+ logfile.write(tb)
+
+ def paste(self, lodgeit_url):
+ """Create a paste and return the paste id."""
+ from xmlrpclib import ServerProxy
+ srv = ServerProxy('%sxmlrpc/' % lodgeit_url)
+ return srv.pastes.newPaste('pytb', self.plaintext, '', '', '', True)
+
+ def render_summary(self, include_title=True):
+ """Render the traceback for the interactive console."""
+ title = ''
+ description = ''
+ frames = []
+ classes = ['traceback']
+ if not self.frames:
+ classes.append('noframe-traceback')
+
+ if include_title:
+ if self.is_syntax_error:
+ title = u'Syntax Error'
+ else:
+ title = u'Traceback <em>(most recent call last)</em>:'
+
+ for frame in self.frames:
+ frames.append(u'<li%s>%s' % (
+ frame.info and u' title="%s"' % escape(frame.info) or u'',
+ frame.render()
+ ))
+
+ if self.is_syntax_error:
+ description_wrapper = u'<pre class=syntaxerror>%s</pre>'
+ else:
+ description_wrapper = u'<blockquote>%s</blockquote>'
+
+ return SUMMARY_HTML % {
+ 'classes': u' '.join(classes),
+ 'title': title and u'<h3>%s</h3>' % title or u'',
+ 'frames': u'\n'.join(frames),
+ 'description': description_wrapper % escape(self.exception)
+ }
+
+ def render_full(self, evalex=False, lodgeit_url=None,
+ secret=None):
+ """Render the Full HTML page with the traceback info."""
+ exc = escape(self.exception)
+ return PAGE_HTML % {
+ 'evalex': evalex and 'true' or 'false',
+ 'console': 'false',
+ 'lodgeit_url': escape(lodgeit_url),
+ 'title': exc,
+ 'exception': exc,
+ 'exception_type': escape(self.exception_type),
+ 'summary': self.render_summary(include_title=False),
+ 'plaintext': self.plaintext,
+ 'plaintext_cs': re.sub('-{2,}', '-', self.plaintext),
+ 'traceback_id': self.id,
+ 'secret': secret
+ }
+
+ def generate_plaintext_traceback(self):
+ """Like the plaintext attribute but returns a generator"""
+ yield u'Traceback (most recent call last):'
+ for frame in self.frames:
+ yield u' File "%s", line %s, in %s' % (
+ frame.filename,
+ frame.lineno,
+ frame.function_name
+ )
+ yield u' ' + frame.current_line.strip()
+ yield self.exception
+
+ def plaintext(self):
+ return u'\n'.join(self.generate_plaintext_traceback())
+ plaintext = cached_property(plaintext)
+
+ id = property(lambda x: id(x))
+
+
+class Frame(object):
+ """A single frame in a traceback."""
+
+ def __init__(self, exc_type, exc_value, tb):
+ self.lineno = tb.tb_lineno
+ self.function_name = tb.tb_frame.f_code.co_name
+ self.locals = tb.tb_frame.f_locals
+ self.globals = tb.tb_frame.f_globals
+
+ fn = inspect.getsourcefile(tb) or inspect.getfile(tb)
+ if fn[-4:] in ('.pyo', '.pyc'):
+ fn = fn[:-1]
+ # if it's a file on the file system resolve the real filename.
+ if os.path.isfile(fn):
+ fn = os.path.realpath(fn)
+ self.filename = fn
+ self.module = self.globals.get('__name__')
+ self.loader = self.globals.get('__loader__')
+ self.code = tb.tb_frame.f_code
+
+ # support for paste's traceback extensions
+ self.hide = self.locals.get('__traceback_hide__', False)
+ info = self.locals.get('__traceback_info__')
+ if info is not None:
+ try:
+ info = unicode(info)
+ except UnicodeError:
+ info = str(info).decode('utf-8', 'replace')
+ self.info = info
+
+ def render(self):
+ """Render a single frame in a traceback."""
+ return FRAME_HTML % {
+ 'id': self.id,
+ 'filename': escape(self.filename),
+ 'lineno': self.lineno,
+ 'function_name': escape(self.function_name),
+ 'current_line': escape(self.current_line.strip())
+ }
+
+ def get_annotated_lines(self):
+ """Helper function that returns lines with extra information."""
+ lines = [Line(idx + 1, x) for idx, x in enumerate(self.sourcelines)]
+
+ # find function definition and mark lines
+ if hasattr(self.code, 'co_firstlineno'):
+ lineno = self.code.co_firstlineno - 1
+ while lineno > 0:
+ if _funcdef_re.match(lines[lineno].code):
+ break
+ lineno -= 1
+ try:
+ offset = len(inspect.getblock([x.code + '\n' for x
+ in lines[lineno:]]))
+ except TokenError:
+ offset = 0
+ for line in lines[lineno:lineno + offset]:
+ line.in_frame = True
+
+ # mark current line
+ try:
+ lines[self.lineno - 1].current = True
+ except IndexError:
+ pass
+
+ return lines
+
+ def render_source(self):
+ """Render the sourcecode."""
+ return SOURCE_TABLE_HTML % u'\n'.join(line.render() for line in
+ self.get_annotated_lines())
+
+ def eval(self, code, mode='single'):
+ """Evaluate code in the context of the frame."""
+ if isinstance(code, basestring):
+ if isinstance(code, unicode):
+ code = UTF8_COOKIE + code.encode('utf-8')
+ code = compile(code, '<interactive>', mode)
+ if mode != 'exec':
+ return eval(code, self.globals, self.locals)
+ exec code in self.globals, self.locals
+
+ @cached_property
+ def sourcelines(self):
+ """The sourcecode of the file as list of unicode strings."""
+ # get sourcecode from loader or file
+ source = None
+ if self.loader is not None:
+ try:
+ if hasattr(self.loader, 'get_source'):
+ source = self.loader.get_source(self.module)
+ elif hasattr(self.loader, 'get_source_by_code'):
+ source = self.loader.get_source_by_code(self.code)
+ except Exception:
+ # we munch the exception so that we don't cause troubles
+ # if the loader is broken.
+ pass
+
+ if source is None:
+ try:
+ f = file(self.filename)
+ except IOError:
+ return []
+ try:
+ source = f.read()
+ finally:
+ f.close()
+
+ # already unicode? return right away
+ if isinstance(source, unicode):
+ return source.splitlines()
+
+ # yes. it should be ascii, but we don't want to reject too many
+ # characters in the debugger if something breaks
+ charset = 'utf-8'
+ if source.startswith(UTF8_COOKIE):
+ source = source[3:]
+ else:
+ for idx, match in enumerate(_line_re.finditer(source)):
+ match = _line_re.search(match.group())
+ if match is not None:
+ charset = match.group(1)
+ break
+ if idx > 1:
+ break
+
+ # on broken cookies we fall back to utf-8 too
+ try:
+ codecs.lookup(charset)
+ except LookupError:
+ charset = 'utf-8'
+
+ return source.decode(charset, 'replace').splitlines()
+
+ @property
+ def current_line(self):
+ try:
+ return self.sourcelines[self.lineno - 1]
+ except IndexError:
+ return u''
+
+ @cached_property
+ def console(self):
+ return Console(self.globals, self.locals)
+
+ id = property(lambda x: id(x))
diff --git a/websdk/werkzeug/exceptions.py b/websdk/werkzeug/exceptions.py
new file mode 100644
index 0000000..b9b43c9
--- /dev/null
+++ b/websdk/werkzeug/exceptions.py
@@ -0,0 +1,536 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.exceptions
+ ~~~~~~~~~~~~~~~~~~~
+
+ This module implements a number of Python exceptions you can raise from
+ within your views to trigger a standard non-200 response.
+
+
+ Usage Example
+ -------------
+
+ ::
+
+ from werkzeug.wrappers import BaseRequest
+ from werkzeug.wsgi import responder
+ from werkzeug.exceptions import HTTPException, NotFound
+
+ def view(request):
+ raise NotFound()
+
+ @responder
+ def application(environ, start_response):
+ request = BaseRequest(environ)
+ try:
+ return view(request)
+ except HTTPException, e:
+ return e
+
+
+ As you can see from this example those exceptions are callable WSGI
+ applications. Because of Python 2.4 compatibility those do not extend
+ from the response objects but only from the python exception class.
+
+ As a matter of fact they are not Werkzeug response objects. However you
+ can get a response object by calling ``get_response()`` on a HTTP
+ exception.
+
+ Keep in mind that you have to pass an environment to ``get_response()``
+ because some errors fetch additional information from the WSGI
+ environment.
+
+ If you want to hook in a different exception page to say, a 404 status
+ code, you can add a second except for a specific subclass of an error::
+
+ @responder
+ def application(environ, start_response):
+ request = BaseRequest(environ)
+ try:
+ return view(request)
+ except NotFound, e:
+ return not_found(request)
+ except HTTPException, e:
+ return e
+
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+from werkzeug._internal import HTTP_STATUS_CODES, _get_environ
+
+
+class HTTPException(Exception):
+ """
+ Baseclass for all HTTP exceptions. This exception can be called as WSGI
+ application to render a default error page or you can catch the subclasses
+ of it independently and render nicer error messages.
+ """
+
+ code = None
+ description = None
+
+ def __init__(self, description=None):
+ Exception.__init__(self, '%d %s' % (self.code, self.name))
+ if description is not None:
+ self.description = description
+
+ @classmethod
+ def wrap(cls, exception, name=None):
+ """This method returns a new subclass of the exception provided that
+ also is a subclass of `BadRequest`.
+ """
+ class newcls(cls, exception):
+ def __init__(self, arg=None, description=None):
+ cls.__init__(self, description)
+ exception.__init__(self, arg)
+ newcls.__module__ = sys._getframe(1).f_globals.get('__name__')
+ newcls.__name__ = name or cls.__name__ + exception.__name__
+ return newcls
+
+ @property
+ def name(self):
+ """The status name."""
+ return HTTP_STATUS_CODES[self.code]
+
+ def get_description(self, environ):
+ """Get the description."""
+ environ = _get_environ(environ)
+ return self.description
+
+ def get_body(self, environ):
+ """Get the HTML body."""
+ return (
+ '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
+ '<title>%(code)s %(name)s</title>\n'
+ '<h1>%(name)s</h1>\n'
+ '%(description)s\n'
+ ) % {
+ 'code': self.code,
+ 'name': escape(self.name),
+ 'description': self.get_description(environ)
+ }
+
+ def get_headers(self, environ):
+ """Get a list of headers."""
+ return [('Content-Type', 'text/html')]
+
+ def get_response(self, environ):
+ """Get a response object.
+
+ :param environ: the environ for the request.
+ :return: a :class:`BaseResponse` object or a subclass thereof.
+ """
+ # lazily imported for various reasons. For one, we can use the exceptions
+ # with custom responses (testing exception instances against types) and
+ # so we don't ever have to import the wrappers, but also because there
+ # are circular dependencies when bootstrapping the module.
+ environ = _get_environ(environ)
+ from werkzeug.wrappers import BaseResponse
+ headers = self.get_headers(environ)
+ return BaseResponse(self.get_body(environ), self.code, headers)
+
+ def __call__(self, environ, start_response):
+ """Call the exception as WSGI application.
+
+ :param environ: the WSGI environment.
+ :param start_response: the response callable provided by the WSGI
+ server.
+ """
+ response = self.get_response(environ)
+ return response(environ, start_response)
+
+ def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
+ if 'description' in self.__dict__:
+ txt = self.description
+ else:
+ txt = self.name
+ return '%d: %s' % (self.code, txt)
+
+ def __repr__(self):
+ return '<%s \'%s\'>' % (self.__class__.__name__, self)
+
+
+class _ProxyException(HTTPException):
+ """An HTTP exception that expands renders a WSGI application on error."""
+
+ def __init__(self, response):
+ Exception.__init__(self, 'proxy exception for %r' % response)
+ self.response = response
+
+ def get_response(self, environ):
+ return self.response
+
+
+class BadRequest(HTTPException):
+ """*400* `Bad Request`
+
+ Raise if the browser sends something to the application the application
+ or server cannot handle.
+ """
+ code = 400
+ description = (
+ '<p>The browser (or proxy) sent a request that this server could '
+ 'not understand.</p>'
+ )
+
+
+class ClientDisconnected(BadRequest):
+ """Internal exception that is raised if Werkzeug detects a disconnected
+ client. Since the client is already gone at that point attempting to
+ send the error message to the client might not work and might ultimately
+ result in another exception in the server. Mainly this is here so that
+ it is silenced by default as far as Werkzeug is concerned.
+
+ Since disconnections cannot be reliably detected and are unspecified
+ by WSGI to a large extend this might or might not be raised if a client
+ is gone.
+
+ .. versionadded:: 0.8
+ """
+
+
+class Unauthorized(HTTPException):
+ """*401* `Unauthorized`
+
+ Raise if the user is not authorized. Also used if you want to use HTTP
+ basic auth.
+ """
+ code = 401
+ description = (
+ '<p>The server could not verify that you are authorized to access '
+ 'the URL requested. You either supplied the wrong credentials (e.g. '
+ 'a bad password), or your browser doesn\'t understand how to supply '
+ 'the credentials required.</p><p>In case you are allowed to request '
+ 'the document, please check your user-id and password and try '
+ 'again.</p>'
+ )
+
+
+class Forbidden(HTTPException):
+ """*403* `Forbidden`
+
+ Raise if the user doesn't have the permission for the requested resource
+ but was authenticated.
+ """
+ code = 403
+ description = (
+ '<p>You don\'t have the permission to access the requested resource. '
+ 'It is either read-protected or not readable by the server.</p>'
+ )
+
+
+class NotFound(HTTPException):
+ """*404* `Not Found`
+
+ Raise if a resource does not exist and never existed.
+ """
+ code = 404
+ description = (
+ '<p>The requested URL was not found on the server.</p>'
+ '<p>If you entered the URL manually please check your spelling and '
+ 'try again.</p>'
+ )
+
+
+class MethodNotAllowed(HTTPException):
+ """*405* `Method Not Allowed`
+
+ Raise if the server used a method the resource does not handle. For
+ example `POST` if the resource is view only. Especially useful for REST.
+
+ The first argument for this exception should be a list of allowed methods.
+ Strictly speaking the response would be invalid if you don't provide valid
+ methods in the header which you can do with that list.
+ """
+ code = 405
+
+ def __init__(self, valid_methods=None, description=None):
+ """Takes an optional list of valid http methods
+ starting with werkzeug 0.3 the list will be mandatory."""
+ HTTPException.__init__(self, description)
+ self.valid_methods = valid_methods
+
+ def get_headers(self, environ):
+ headers = HTTPException.get_headers(self, environ)
+ if self.valid_methods:
+ headers.append(('Allow', ', '.join(self.valid_methods)))
+ return headers
+
+ def get_description(self, environ):
+ m = escape(environ.get('REQUEST_METHOD', 'GET'))
+ return '<p>The method %s is not allowed for the requested URL.</p>' % m
+
+
+class NotAcceptable(HTTPException):
+ """*406* `Not Acceptable`
+
+ Raise if the server can't return any content conforming to the
+ `Accept` headers of the client.
+ """
+ code = 406
+
+ description = (
+ '<p>The resource identified by the request is only capable of '
+ 'generating response entities which have content characteristics '
+ 'not acceptable according to the accept headers sent in the '
+ 'request.</p>'
+ )
+
+
+class RequestTimeout(HTTPException):
+ """*408* `Request Timeout`
+
+ Raise to signalize a timeout.
+ """
+ code = 408
+ description = (
+ '<p>The server closed the network connection because the browser '
+ 'didn\'t finish the request within the specified time.</p>'
+ )
+
+
+class Conflict(HTTPException):
+ """*409* `Conflict`
+
+ Raise to signal that a request cannot be completed because it conflicts
+ with the current state on the server.
+
+ .. versionadded:: 0.7
+ """
+ code = 409
+ description = (
+ '<p>A conflict happened while processing the request. The resource '
+ 'might have been modified while the request was being processed.'
+ )
+
+
+class Gone(HTTPException):
+ """*410* `Gone`
+
+ Raise if a resource existed previously and went away without new location.
+ """
+ code = 410
+ description = (
+ '<p>The requested URL is no longer available on this server and '
+ 'there is no forwarding address.</p><p>If you followed a link '
+ 'from a foreign page, please contact the author of this page.'
+ )
+
+
+class LengthRequired(HTTPException):
+ """*411* `Length Required`
+
+ Raise if the browser submitted data but no ``Content-Length`` header which
+ is required for the kind of processing the server does.
+ """
+ code = 411
+ description = (
+ '<p>A request with this method requires a valid <code>Content-'
+ 'Length</code> header.</p>'
+ )
+
+
+class PreconditionFailed(HTTPException):
+ """*412* `Precondition Failed`
+
+ Status code used in combination with ``If-Match``, ``If-None-Match``, or
+ ``If-Unmodified-Since``.
+ """
+ code = 412
+ description = (
+ '<p>The precondition on the request for the URL failed positive '
+ 'evaluation.</p>'
+ )
+
+
+class RequestEntityTooLarge(HTTPException):
+ """*413* `Request Entity Too Large`
+
+ The status code one should return if the data submitted exceeded a given
+ limit.
+ """
+ code = 413
+ description = (
+ '<p>The data value transmitted exceeds the capacity limit.</p>'
+ )
+
+
+class RequestURITooLarge(HTTPException):
+ """*414* `Request URI Too Large`
+
+ Like *413* but for too long URLs.
+ """
+ code = 414
+ description = (
+ '<p>The length of the requested URL exceeds the capacity limit '
+ 'for this server. The request cannot be processed.</p>'
+ )
+
+
+class UnsupportedMediaType(HTTPException):
+ """*415* `Unsupported Media Type`
+
+ The status code returned if the server is unable to handle the media type
+ the client transmitted.
+ """
+ code = 415
+ description = (
+ '<p>The server does not support the media type transmitted in '
+ 'the request.</p>'
+ )
+
+
+class RequestedRangeNotSatisfiable(HTTPException):
+ """*416* `Requested Range Not Satisfiable`
+
+ The client asked for a part of the file that lies beyond the end
+ of the file.
+
+ .. versionadded:: 0.7
+ """
+ code = 416
+ description = (
+ '<p>The server cannot provide the requested range.'
+ )
+
+
+class ExpectationFailed(HTTPException):
+ """*417* `Expectation Failed`
+
+ The server cannot meet the requirements of the Expect request-header.
+
+ .. versionadded:: 0.7
+ """
+ code = 417
+ description = (
+ '<p>The server could not meet the requirements of the Expect header'
+ )
+
+
+class ImATeapot(HTTPException):
+ """*418* `I'm a teapot`
+
+ The server should return this if it is a teapot and someone attempted
+ to brew coffee with it.
+
+ .. versionadded:: 0.7
+ """
+ code = 418
+ description = (
+ '<p>This server is a teapot, not a coffee machine'
+ )
+
+
+class InternalServerError(HTTPException):
+ """*500* `Internal Server Error`
+
+ Raise if an internal server error occurred. This is a good fallback if an
+ unknown error occurred in the dispatcher.
+ """
+ code = 500
+ description = (
+ '<p>The server encountered an internal error and was unable to '
+ 'complete your request. Either the server is overloaded or there '
+ 'is an error in the application.</p>'
+ )
+
+
+class NotImplemented(HTTPException):
+ """*501* `Not Implemented`
+
+ Raise if the application does not support the action requested by the
+ browser.
+ """
+ code = 501
+ description = (
+ '<p>The server does not support the action requested by the '
+ 'browser.</p>'
+ )
+
+
+class BadGateway(HTTPException):
+ """*502* `Bad Gateway`
+
+ If you do proxying in your application you should return this status code
+ if you received an invalid response from the upstream server it accessed
+ in attempting to fulfill the request.
+ """
+ code = 502
+ description = (
+ '<p>The proxy server received an invalid response from an upstream '
+ 'server.</p>'
+ )
+
+
+class ServiceUnavailable(HTTPException):
+ """*503* `Service Unavailable`
+
+ Status code you should return if a service is temporarily unavailable.
+ """
+ code = 503
+ description = (
+ '<p>The server is temporarily unable to service your request due to '
+ 'maintenance downtime or capacity problems. Please try again '
+ 'later.</p>'
+ )
+
+
+default_exceptions = {}
+__all__ = ['HTTPException']
+
+def _find_exceptions():
+ for name, obj in globals().iteritems():
+ try:
+ if getattr(obj, 'code', None) is not None:
+ default_exceptions[obj.code] = obj
+ __all__.append(obj.__name__)
+ except TypeError: # pragma: no cover
+ continue
+_find_exceptions()
+del _find_exceptions
+
+
+#: raised by the request functions if they were unable to decode the
+#: incoming data properly.
+HTTPUnicodeError = BadRequest.wrap(UnicodeError, 'HTTPUnicodeError')
+
+
+class Aborter(object):
+ """
+ When passed a dict of code -> exception items it can be used as
+ callable that raises exceptions. If the first argument to the
+ callable is an integer it will be looked up in the mapping, if it's
+ a WSGI application it will be raised in a proxy exception.
+
+ The rest of the arguments are forwarded to the exception constructor.
+ """
+
+ def __init__(self, mapping=None, extra=None):
+ if mapping is None:
+ mapping = default_exceptions
+ self.mapping = dict(mapping)
+ if extra is not None:
+ self.mapping.update(extra)
+
+ def __call__(self, code, *args, **kwargs):
+ if not args and not kwargs and not isinstance(code, (int, long)):
+ raise _ProxyException(code)
+ if code not in self.mapping:
+ raise LookupError('no exception for %r' % code)
+ raise self.mapping[code](*args, **kwargs)
+
+abort = Aborter()
+
+
+#: an exception that is used internally to signal both a key error and a
+#: bad request. Used by a lot of the datastructures.
+BadRequestKeyError = BadRequest.wrap(KeyError)
+
+
+# imported here because of circular dependencies of werkzeug.utils
+from werkzeug.utils import escape
diff --git a/websdk/werkzeug/formparser.py b/websdk/werkzeug/formparser.py
new file mode 100644
index 0000000..cd57620
--- /dev/null
+++ b/websdk/werkzeug/formparser.py
@@ -0,0 +1,463 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.formparser
+ ~~~~~~~~~~~~~~~~~~~
+
+ This module implements the form parsing. It supports url-encoded forms
+ as well as non-nested multipart uploads.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+from cStringIO import StringIO
+from tempfile import TemporaryFile
+from itertools import chain, repeat
+from functools import update_wrapper
+
+from werkzeug._internal import _decode_unicode, _empty_stream
+from werkzeug.urls import url_decode_stream
+from werkzeug.wsgi import LimitedStream, make_line_iter
+from werkzeug.exceptions import RequestEntityTooLarge
+from werkzeug.datastructures import Headers, FileStorage, MultiDict
+from werkzeug.http import parse_options_header
+
+
+#: an iterator that yields empty strings
+_empty_string_iter = repeat('')
+
+#: a regular expression for multipart boundaries
+_multipart_boundary_re = re.compile('^[ -~]{0,200}[!-~]$')
+
+#: supported http encodings that are also available in python we support
+#: for multipart messages.
+_supported_multipart_encodings = frozenset(['base64', 'quoted-printable'])
+
+
+def default_stream_factory(total_content_length, filename, content_type,
+ content_length=None):
+ """The stream factory that is used per default."""
+ if total_content_length > 1024 * 500:
+ return TemporaryFile('wb+')
+ return StringIO()
+
+
+def parse_form_data(environ, stream_factory=None, charset='utf-8',
+ errors='replace', max_form_memory_size=None,
+ max_content_length=None, cls=None,
+ silent=True):
+ """Parse the form data in the environ and return it as tuple in the form
+ ``(stream, form, files)``. You should only call this method if the
+ transport method is `POST`, `PUT`, or `PATCH`.
+
+ If the mimetype of the data transmitted is `multipart/form-data` the
+ files multidict will be filled with `FileStorage` objects. If the
+ mimetype is unknown the input stream is wrapped and returned as first
+ argument, else the stream is empty.
+
+ This is a shortcut for the common usage of :class:`FormDataParser`.
+
+ Have a look at :ref:`dealing-with-request-data` for more details.
+
+ .. versionadded:: 0.5
+ The `max_form_memory_size`, `max_content_length` and
+ `cls` parameters were added.
+
+ .. versionadded:: 0.5.1
+ The optional `silent` flag was added.
+
+ :param environ: the WSGI environment to be used for parsing.
+ :param stream_factory: An optional callable that returns a new read and
+ writeable file descriptor. This callable works
+ the same as :meth:`~BaseResponse._get_file_stream`.
+ :param charset: The character set for URL and url encoded form data.
+ :param errors: The encoding error behavior.
+ :param max_form_memory_size: the maximum number of bytes to be accepted for
+ in-memory stored form data. If the data
+ exceeds the value specified an
+ :exc:`~exceptions.RequestURITooLarge`
+ exception is raised.
+ :param max_content_length: If this is provided and the transmitted data
+ is longer than this value an
+ :exc:`~exceptions.RequestEntityTooLarge`
+ exception is raised.
+ :param cls: an optional dict class to use. If this is not specified
+ or `None` the default :class:`MultiDict` is used.
+ :param silent: If set to False parsing errors will not be caught.
+ :return: A tuple in the form ``(stream, form, files)``.
+ """
+ return FormDataParser(stream_factory, charset, errors,
+ max_form_memory_size, max_content_length,
+ cls, silent).parse_from_environ(environ)
+
+
+def exhaust_stream(f):
+ """Helper decorator for methods that exhausts the stream on return."""
+ def wrapper(self, stream, *args, **kwargs):
+ try:
+ return f(self, stream, *args, **kwargs)
+ finally:
+ stream.exhaust()
+ return update_wrapper(wrapper, f)
+
+
+class FormDataParser(object):
+ """This class implements parsing of form data for Werkzeug. By itself
+ it can parse multipart and url encoded form data. It can be subclasses
+ and extended but for most mimetypes it is a better idea to use the
+ untouched stream and expose it as separate attributes on a request
+ object.
+
+ .. versionadded:: 0.8
+
+ :param stream_factory: An optional callable that returns a new read and
+ writeable file descriptor. This callable works
+ the same as :meth:`~BaseResponse._get_file_stream`.
+ :param charset: The character set for URL and url encoded form data.
+ :param errors: The encoding error behavior.
+ :param max_form_memory_size: the maximum number of bytes to be accepted for
+ in-memory stored form data. If the data
+ exceeds the value specified an
+ :exc:`~exceptions.RequestURITooLarge`
+ exception is raised.
+ :param max_content_length: If this is provided and the transmitted data
+ is longer than this value an
+ :exc:`~exceptions.RequestEntityTooLarge`
+ exception is raised.
+ :param cls: an optional dict class to use. If this is not specified
+ or `None` the default :class:`MultiDict` is used.
+ :param silent: If set to False parsing errors will not be caught.
+ """
+
+ def __init__(self, stream_factory=None, charset='utf-8',
+ errors='replace', max_form_memory_size=None,
+ max_content_length=None, cls=None,
+ silent=True):
+ if stream_factory is None:
+ stream_factory = default_stream_factory
+ self.stream_factory = stream_factory
+ self.charset = charset
+ self.errors = errors
+ self.max_form_memory_size = max_form_memory_size
+ self.max_content_length = max_content_length
+ if cls is None:
+ cls = MultiDict
+ self.cls = cls
+ self.silent = silent
+
+ def get_parse_func(self, mimetype, options):
+ return self.parse_functions.get(mimetype)
+
+ def parse_from_environ(self, environ):
+ """Parses the information from the environment as form data.
+
+ :param environ: the WSGI environment to be used for parsing.
+ :return: A tuple in the form ``(stream, form, files)``.
+ """
+ content_type = environ.get('CONTENT_TYPE', '')
+ mimetype, options = parse_options_header(content_type)
+ try:
+ content_length = int(environ['CONTENT_LENGTH'])
+ except (KeyError, ValueError):
+ content_length = 0
+ stream = environ['wsgi.input']
+ return self.parse(stream, mimetype, content_length, options)
+
+ def parse(self, stream, mimetype, content_length, options=None):
+ """Parses the information from the given stream, mimetype,
+ content length and mimetype parameters.
+
+ :param stream: an input stream
+ :param mimetype: the mimetype of the data
+ :param content_length: the content length of the incoming data
+ :param options: optional mimetype parameters (used for
+ the multipart boundary for instance)
+ :return: A tuple in the form ``(stream, form, files)``.
+ """
+ if self.max_content_length is not None and \
+ content_length > self.max_content_length:
+ raise RequestEntityTooLarge()
+ if options is None:
+ options = {}
+ input_stream = LimitedStream(stream, content_length)
+
+ parse_func = self.get_parse_func(mimetype, options)
+ if parse_func is not None:
+ try:
+ return parse_func(self, input_stream, mimetype,
+ content_length, options)
+ except ValueError:
+ if not self.silent:
+ raise
+ return input_stream, self.cls(), self.cls()
+
+ @exhaust_stream
+ def _parse_multipart(self, stream, mimetype, content_length, options):
+ parser = MultiPartParser(self.stream_factory, self.charset, self.errors,
+ max_form_memory_size=self.max_form_memory_size,
+ cls=self.cls)
+ form, files = parser.parse(stream, options.get('boundary'),
+ content_length)
+ return _empty_stream, form, files
+
+ @exhaust_stream
+ def _parse_urlencoded(self, stream, mimetype, content_length, options):
+ if self.max_form_memory_size is not None and \
+ content_length > self.max_form_memory_size:
+ raise RequestEntityTooLarge()
+ form = url_decode_stream(stream, self.charset,
+ errors=self.errors, cls=self.cls)
+ return _empty_stream, form, self.cls()
+
+ #: mapping of mimetypes to parsing functions
+ parse_functions = {
+ 'multipart/form-data': _parse_multipart,
+ 'application/x-www-form-urlencoded': _parse_urlencoded,
+ 'application/x-url-encoded': _parse_urlencoded
+ }
+
+
+def is_valid_multipart_boundary(boundary):
+ """Checks if the string given is a valid multipart boundary."""
+ return _multipart_boundary_re.match(boundary) is not None
+
+
+def _line_parse(line):
+ """Removes line ending characters and returns a tuple (`stripped_line`,
+ `is_terminated`).
+ """
+ if line[-2:] == '\r\n':
+ return line[:-2], True
+ elif line[-1:] in '\r\n':
+ return line[:-1], True
+ return line, False
+
+
+def parse_multipart_headers(iterable):
+ """Parses multipart headers from an iterable that yields lines (including
+ the trailing newline symbol. The iterable has to be newline terminated:
+
+ >>> parse_multipart_headers(['Foo: Bar\r\n', 'Test: Blub\r\n',
+ ... '\r\n', 'More data'])
+ Headers([('Foo', 'Bar'), ('Test', 'Blub')])
+
+ :param iterable: iterable of strings that are newline terminated
+ """
+ result = []
+ for line in iterable:
+ line, line_terminated = _line_parse(line)
+ if not line_terminated:
+ raise ValueError('unexpected end of line in multipart header')
+ if not line:
+ break
+ elif line[0] in ' \t' and result:
+ key, value = result[-1]
+ result[-1] = (key, value + '\n ' + line[1:])
+ else:
+ parts = line.split(':', 1)
+ if len(parts) == 2:
+ result.append((parts[0].strip(), parts[1].strip()))
+
+ # we link the list to the headers, no need to create a copy, the
+ # list was not shared anyways.
+ return Headers.linked(result)
+
+
+class MultiPartParser(object):
+
+ def __init__(self, stream_factory=None, charset='utf-8', errors='replace',
+ max_form_memory_size=None, cls=None, buffer_size=10 * 1024):
+ self.stream_factory = stream_factory
+ self.charset = charset
+ self.errors = errors
+ self.max_form_memory_size = max_form_memory_size
+ if stream_factory is None:
+ stream_factory = default_stream_factory
+ if cls is None:
+ cls = MultiDict
+ self.cls = cls
+
+ # make sure the buffer size is divisible by four so that we can base64
+ # decode chunk by chunk
+ assert buffer_size % 4 == 0, 'buffer size has to be divisible by 4'
+ # also the buffer size has to be at least 1024 bytes long or long headers
+ # will freak out the system
+ assert buffer_size >= 1024, 'buffer size has to be at least 1KB'
+
+ self.buffer_size = buffer_size
+
+ def _fix_ie_filename(self, filename):
+ """Internet Explorer 6 transmits the full file name if a file is
+ uploaded. This function strips the full path if it thinks the
+ filename is Windows-like absolute.
+ """
+ if filename[1:3] == ':\\' or filename[:2] == '\\\\':
+ return filename.split('\\')[-1]
+ return filename
+
+ def _find_terminator(self, iterator):
+ """The terminator might have some additional newlines before it.
+ There is at least one application that sends additional newlines
+ before headers (the python setuptools package).
+ """
+ for line in iterator:
+ if not line:
+ break
+ line = line.strip()
+ if line:
+ return line
+ return ''
+
+ def fail(self, message):
+ raise ValueError(message)
+
+ def get_part_encoding(self, headers):
+ transfer_encoding = headers.get('content-transfer-encoding')
+ if transfer_encoding is not None and \
+ transfer_encoding in _supported_multipart_encodings:
+ return transfer_encoding
+
+ def get_part_charset(self, headers):
+ # Figure out input charset for current part
+ content_type = headers.get('content-type')
+ if content_type:
+ mimetype, ct_params = parse_options_header(content_type)
+ return ct_params.get('charset', self.charset)
+ return self.charset
+
+ def start_file_streaming(self, filename, headers, total_content_length):
+ filename = _decode_unicode(filename, self.charset, self.errors)
+ filename = self._fix_ie_filename(filename)
+ content_type = headers.get('content_type')
+ try:
+ content_length = int(headers['content-length'])
+ except (KeyError, ValueError):
+ content_length = 0
+ container = self.stream_factory(total_content_length, content_type,
+ filename, content_length)
+ return filename, container
+
+ def in_memory_threshold_reached(self, bytes):
+ raise RequestEntityTooLarge()
+
+ def validate_boundary(self, boundary):
+ if not boundary:
+ self.fail('Missing boundary')
+ if not is_valid_multipart_boundary(boundary):
+ self.fail('Invalid boundary: %s' % boundary)
+ if len(boundary) > self.buffer_size: # pragma: no cover
+ # this should never happen because we check for a minimum size
+ # of 1024 and boundaries may not be longer than 200. The only
+ # situation when this happen is for non debug builds where
+ # the assert i skipped.
+ self.fail('Boundary longer than buffer size')
+
+ def parse(self, file, boundary, content_length):
+ next_part = '--' + boundary
+ last_part = next_part + '--'
+
+ form = []
+ files = []
+ in_memory = 0
+
+ iterator = chain(make_line_iter(file, limit=content_length,
+ buffer_size=self.buffer_size),
+ _empty_string_iter)
+
+ terminator = self._find_terminator(iterator)
+ if terminator != next_part:
+ self.fail('Expected boundary at start of multipart data')
+
+ while terminator != last_part:
+ headers = parse_multipart_headers(iterator)
+
+ disposition = headers.get('content-disposition')
+ if disposition is None:
+ self.fail('Missing Content-Disposition header')
+ disposition, extra = parse_options_header(disposition)
+ transfer_encoding = self.get_part_encoding(headers)
+ name = extra.get('name')
+ filename = extra.get('filename')
+ part_charset = self.get_part_charset(headers)
+
+ # if no content type is given we stream into memory. A list is
+ # used as a temporary container.
+ if filename is None:
+ is_file = False
+ container = []
+ _write = container.append
+ guard_memory = self.max_form_memory_size is not None
+
+ # otherwise we parse the rest of the headers and ask the stream
+ # factory for something we can write in.
+ else:
+ is_file = True
+ guard_memory = False
+ filename, container = self.start_file_streaming(
+ filename, headers, content_length)
+ _write = container.write
+
+ buf = ''
+ for line in iterator:
+ if not line:
+ self.fail('unexpected end of stream')
+
+ if line[:2] == '--':
+ terminator = line.rstrip()
+ if terminator in (next_part, last_part):
+ break
+
+ if transfer_encoding is not None:
+ try:
+ line = line.decode(transfer_encoding)
+ except Exception:
+ self.fail('could not decode transfer encoded chunk')
+
+ # we have something in the buffer from the last iteration.
+ # this is usually a newline delimiter.
+ if buf:
+ _write(buf)
+ buf = ''
+
+ # If the line ends with windows CRLF we write everything except
+ # the last two bytes. In all other cases however we write
+ # everything except the last byte. If it was a newline, that's
+ # fine, otherwise it does not matter because we will write it
+ # the next iteration. this ensures we do not write the
+ # final newline into the stream. That way we do not have to
+ # truncate the stream. However we do have to make sure that
+ # if something else than a newline is in there we write it
+ # out.
+ if line[-2:] == '\r\n':
+ buf = '\r\n'
+ cutoff = -2
+ else:
+ buf = line[-1]
+ cutoff = -1
+ _write(line[:cutoff])
+
+ # if we write into memory and there is a memory size limit we
+ # count the number of bytes in memory and raise an exception if
+ # there is too much data in memory.
+ if guard_memory:
+ in_memory += len(line)
+ if in_memory > self.max_form_memory_size:
+ self.in_memory_threshold_reached(in_memory)
+ else: # pragma: no cover
+ raise ValueError('unexpected end of part')
+
+ # if we have a leftover in the buffer that is not a newline
+ # character we have to flush it, otherwise we will chop of
+ # certain values.
+ if buf not in ('', '\r', '\n', '\r\n'):
+ _write(buf)
+
+ if is_file:
+ container.seek(0)
+ files.append((name, FileStorage(container, filename, name,
+ headers=headers)))
+ else:
+ form.append((name, _decode_unicode(''.join(container),
+ part_charset, self.errors)))
+
+ return self.cls(form), self.cls(files)
diff --git a/websdk/werkzeug/http.py b/websdk/werkzeug/http.py
new file mode 100644
index 0000000..23e1328
--- /dev/null
+++ b/websdk/werkzeug/http.py
@@ -0,0 +1,841 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.http
+ ~~~~~~~~~~~~~
+
+ Werkzeug comes with a bunch of utilities that help Werkzeug to deal with
+ HTTP data. Most of the classes and functions provided by this module are
+ used by the wrappers, but they are useful on their own, too, especially if
+ the response and request objects are not used.
+
+ This covers some of the more HTTP centric features of WSGI, some other
+ utilities such as cookie handling are documented in the `werkzeug.utils`
+ module.
+
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+from time import time
+try:
+ from email.utils import parsedate_tz
+except ImportError: # pragma: no cover
+ from email.Utils import parsedate_tz
+from urllib2 import parse_http_list as _parse_list_header
+from datetime import datetime, timedelta
+try:
+ from hashlib import md5
+except ImportError: # pragma: no cover
+ from md5 import new as md5
+
+
+#: HTTP_STATUS_CODES is "exported" from this module.
+#: XXX: move to werkzeug.consts or something
+from werkzeug._internal import HTTP_STATUS_CODES, _dump_date, \
+ _ExtendedCookie, _ExtendedMorsel, _decode_unicode
+
+
+_accept_re = re.compile(r'([^\s;,]+)(?:[^,]*?;\s*q=(\d*(?:\.\d+)?))?')
+_token_chars = frozenset("!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ '^_`abcdefghijklmnopqrstuvwxyz|~')
+_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)')
+_unsafe_header_chars = set('()<>@,;:\"/[]?={} \t')
+_quoted_string_re = r'"[^"\\]*(?:\\.[^"\\]*)*"'
+_option_header_piece_re = re.compile(r';\s*([^\s;=]+|%s)\s*(?:=\s*([^;]+|%s))?\s*' %
+ (_quoted_string_re, _quoted_string_re))
+
+_entity_headers = frozenset([
+ 'allow', 'content-encoding', 'content-language', 'content-length',
+ 'content-location', 'content-md5', 'content-range', 'content-type',
+ 'expires', 'last-modified'
+])
+_hop_by_hop_headers = frozenset([
+ 'connection', 'keep-alive', 'proxy-authenticate',
+ 'proxy-authorization', 'te', 'trailers', 'transfer-encoding',
+ 'upgrade'
+])
+
+
+def quote_header_value(value, extra_chars='', allow_token=True):
+ """Quote a header value if necessary.
+
+ .. versionadded:: 0.5
+
+ :param value: the value to quote.
+ :param extra_chars: a list of extra characters to skip quoting.
+ :param allow_token: if this is enabled token values are returned
+ unchanged.
+ """
+ value = str(value)
+ if allow_token:
+ token_chars = _token_chars | set(extra_chars)
+ if set(value).issubset(token_chars):
+ return value
+ return '"%s"' % value.replace('\\', '\\\\').replace('"', '\\"')
+
+
+def unquote_header_value(value, is_filename=False):
+ r"""Unquotes a header value. (Reversal of :func:`quote_header_value`).
+ This does not use the real unquoting but what browsers are actually
+ using for quoting.
+
+ .. versionadded:: 0.5
+
+ :param value: the header value to unquote.
+ """
+ if value and value[0] == value[-1] == '"':
+ # this is not the real unquoting, but fixing this so that the
+ # RFC is met will result in bugs with internet explorer and
+ # probably some other browsers as well. IE for example is
+ # uploading files with "C:\foo\bar.txt" as filename
+ value = value[1:-1]
+
+ # if this is a filename and the starting characters look like
+ # a UNC path, then just return the value without quotes. Using the
+ # replace sequence below on a UNC path has the effect of turning
+ # the leading double slash into a single slash and then
+ # _fix_ie_filename() doesn't work correctly. See #458.
+ if not is_filename or value[:2] != '\\\\':
+ return value.replace('\\\\', '\\').replace('\\"', '"')
+ return value
+
+
+def dump_options_header(header, options):
+ """The reverse function to :func:`parse_options_header`.
+
+ :param header: the header to dump
+ :param options: a dict of options to append.
+ """
+ segments = []
+ if header is not None:
+ segments.append(header)
+ for key, value in options.iteritems():
+ if value is None:
+ segments.append(key)
+ else:
+ segments.append('%s=%s' % (key, quote_header_value(value)))
+ return '; '.join(segments)
+
+
+def dump_header(iterable, allow_token=True):
+ """Dump an HTTP header again. This is the reversal of
+ :func:`parse_list_header`, :func:`parse_set_header` and
+ :func:`parse_dict_header`. This also quotes strings that include an
+ equals sign unless you pass it as dict of key, value pairs.
+
+ >>> dump_header({'foo': 'bar baz'})
+ 'foo="bar baz"'
+ >>> dump_header(('foo', 'bar baz'))
+ 'foo, "bar baz"'
+
+ :param iterable: the iterable or dict of values to quote.
+ :param allow_token: if set to `False` tokens as values are disallowed.
+ See :func:`quote_header_value` for more details.
+ """
+ if isinstance(iterable, dict):
+ items = []
+ for key, value in iterable.iteritems():
+ if value is None:
+ items.append(key)
+ else:
+ items.append('%s=%s' % (
+ key,
+ quote_header_value(value, allow_token=allow_token)
+ ))
+ else:
+ items = [quote_header_value(x, allow_token=allow_token)
+ for x in iterable]
+ return ', '.join(items)
+
+
+def parse_list_header(value):
+ """Parse lists as described by RFC 2068 Section 2.
+
+ In particular, parse comma-separated lists where the elements of
+ the list may include quoted-strings. A quoted-string could
+ contain a comma. A non-quoted string could have quotes in the
+ middle. Quotes are removed automatically after parsing.
+
+ It basically works like :func:`parse_set_header` just that items
+ may appear multiple times and case sensitivity is preserved.
+
+ The return value is a standard :class:`list`:
+
+ >>> parse_list_header('token, "quoted value"')
+ ['token', 'quoted value']
+
+ To create a header from the :class:`list` again, use the
+ :func:`dump_header` function.
+
+ :param value: a string with a list header.
+ :return: :class:`list`
+ """
+ result = []
+ for item in _parse_list_header(value):
+ if item[:1] == item[-1:] == '"':
+ item = unquote_header_value(item[1:-1])
+ result.append(item)
+ return result
+
+
+def parse_dict_header(value):
+ """Parse lists of key, value pairs as described by RFC 2068 Section 2 and
+ convert them into a python dict:
+
+ >>> d = parse_dict_header('foo="is a fish", bar="as well"')
+ >>> type(d) is dict
+ True
+ >>> sorted(d.items())
+ [('bar', 'as well'), ('foo', 'is a fish')]
+
+ If there is no value for a key it will be `None`:
+
+ >>> parse_dict_header('key_without_value')
+ {'key_without_value': None}
+
+ To create a header from the :class:`dict` again, use the
+ :func:`dump_header` function.
+
+ :param value: a string with a dict header.
+ :return: :class:`dict`
+ """
+ result = {}
+ for item in _parse_list_header(value):
+ if '=' not in item:
+ result[item] = None
+ continue
+ name, value = item.split('=', 1)
+ if value[:1] == value[-1:] == '"':
+ value = unquote_header_value(value[1:-1])
+ result[name] = value
+ return result
+
+
+def parse_options_header(value):
+ """Parse a ``Content-Type`` like header into a tuple with the content
+ type and the options:
+
+ >>> parse_options_header('Content-Type: text/html; mimetype=text/html')
+ ('Content-Type:', {'mimetype': 'text/html'})
+
+ This should not be used to parse ``Cache-Control`` like headers that use
+ a slightly different format. For these headers use the
+ :func:`parse_dict_header` function.
+
+ .. versionadded:: 0.5
+
+ :param value: the header to parse.
+ :return: (str, options)
+ """
+ def _tokenize(string):
+ for match in _option_header_piece_re.finditer(string):
+ key, value = match.groups()
+ key = unquote_header_value(key)
+ if value is not None:
+ value = unquote_header_value(value, key == 'filename')
+ yield key, value
+
+ if not value:
+ return '', {}
+
+ parts = _tokenize(';' + value)
+ name = parts.next()[0]
+ extra = dict(parts)
+ return name, extra
+
+
+def parse_accept_header(value, cls=None):
+ """Parses an HTTP Accept-* header. This does not implement a complete
+ valid algorithm but one that supports at least value and quality
+ extraction.
+
+ Returns a new :class:`Accept` object (basically a list of ``(value, quality)``
+ tuples sorted by the quality with some additional accessor methods).
+
+ The second parameter can be a subclass of :class:`Accept` that is created
+ with the parsed values and returned.
+
+ :param value: the accept header string to be parsed.
+ :param cls: the wrapper class for the return value (can be
+ :class:`Accept` or a subclass thereof)
+ :return: an instance of `cls`.
+ """
+ if cls is None:
+ cls = Accept
+
+ if not value:
+ return cls(None)
+
+ result = []
+ for match in _accept_re.finditer(value):
+ quality = match.group(2)
+ if not quality:
+ quality = 1
+ else:
+ quality = max(min(float(quality), 1), 0)
+ result.append((match.group(1), quality))
+ return cls(result)
+
+
+def parse_cache_control_header(value, on_update=None, cls=None):
+ """Parse a cache control header. The RFC differs between response and
+ request cache control, this method does not. It's your responsibility
+ to not use the wrong control statements.
+
+ .. versionadded:: 0.5
+ The `cls` was added. If not specified an immutable
+ :class:`~werkzeug.datastructures.RequestCacheControl` is returned.
+
+ :param value: a cache control header to be parsed.
+ :param on_update: an optional callable that is called every time a value
+ on the :class:`~werkzeug.datastructures.CacheControl`
+ object is changed.
+ :param cls: the class for the returned object. By default
+ :class:`~werkzeug.datastructures.RequestCacheControl` is used.
+ :return: a `cls` object.
+ """
+ if cls is None:
+ cls = RequestCacheControl
+ if not value:
+ return cls(None, on_update)
+ return cls(parse_dict_header(value), on_update)
+
+
+def parse_set_header(value, on_update=None):
+ """Parse a set-like header and return a
+ :class:`~werkzeug.datastructures.HeaderSet` object:
+
+ >>> hs = parse_set_header('token, "quoted value"')
+
+ The return value is an object that treats the items case-insensitively
+ and keeps the order of the items:
+
+ >>> 'TOKEN' in hs
+ True
+ >>> hs.index('quoted value')
+ 1
+ >>> hs
+ HeaderSet(['token', 'quoted value'])
+
+ To create a header from the :class:`HeaderSet` again, use the
+ :func:`dump_header` function.
+
+ :param value: a set header to be parsed.
+ :param on_update: an optional callable that is called every time a
+ value on the :class:`~werkzeug.datastructures.HeaderSet`
+ object is changed.
+ :return: a :class:`~werkzeug.datastructures.HeaderSet`
+ """
+ if not value:
+ return HeaderSet(None, on_update)
+ return HeaderSet(parse_list_header(value), on_update)
+
+
+def parse_authorization_header(value):
+ """Parse an HTTP basic/digest authorization header transmitted by the web
+ browser. The return value is either `None` if the header was invalid or
+ not given, otherwise an :class:`~werkzeug.datastructures.Authorization`
+ object.
+
+ :param value: the authorization header to parse.
+ :return: a :class:`~werkzeug.datastructures.Authorization` object or `None`.
+ """
+ if not value:
+ return
+ try:
+ auth_type, auth_info = value.split(None, 1)
+ auth_type = auth_type.lower()
+ except ValueError:
+ return
+ if auth_type == 'basic':
+ try:
+ username, password = auth_info.decode('base64').split(':', 1)
+ except Exception, e:
+ return
+ return Authorization('basic', {'username': username,
+ 'password': password})
+ elif auth_type == 'digest':
+ auth_map = parse_dict_header(auth_info)
+ for key in 'username', 'realm', 'nonce', 'uri', 'response':
+ if not key in auth_map:
+ return
+ if 'qop' in auth_map:
+ if not auth_map.get('nc') or not auth_map.get('cnonce'):
+ return
+ return Authorization('digest', auth_map)
+
+
+def parse_www_authenticate_header(value, on_update=None):
+ """Parse an HTTP WWW-Authenticate header into a
+ :class:`~werkzeug.datastructures.WWWAuthenticate` object.
+
+ :param value: a WWW-Authenticate header to parse.
+ :param on_update: an optional callable that is called every time a value
+ on the :class:`~werkzeug.datastructures.WWWAuthenticate`
+ object is changed.
+ :return: a :class:`~werkzeug.datastructures.WWWAuthenticate` object.
+ """
+ if not value:
+ return WWWAuthenticate(on_update=on_update)
+ try:
+ auth_type, auth_info = value.split(None, 1)
+ auth_type = auth_type.lower()
+ except (ValueError, AttributeError):
+ return WWWAuthenticate(value.strip().lower(), on_update=on_update)
+ return WWWAuthenticate(auth_type, parse_dict_header(auth_info),
+ on_update)
+
+
+def parse_if_range_header(value):
+ """Parses an if-range header which can be an etag or a date. Returns
+ a :class:`~werkzeug.datastructures.IfRange` object.
+
+ .. versionadded:: 0.7
+ """
+ if not value:
+ return IfRange()
+ date = parse_date(value)
+ if date is not None:
+ return IfRange(date=date)
+ # drop weakness information
+ return IfRange(unquote_etag(value)[0])
+
+
+def parse_range_header(value, make_inclusive=True):
+ """Parses a range header into a :class:`~werkzeug.datastructures.Range`
+ object. If the header is missing or malformed `None` is returned.
+ `ranges` is a list of ``(start, stop)`` tuples where the ranges are
+ non-inclusive.
+
+ .. versionadded:: 0.7
+ """
+ if not value or '=' not in value:
+ return None
+
+ ranges = []
+ last_end = 0
+ units, rng = value.split('=', 1)
+ units = units.strip().lower()
+
+ for item in rng.split(','):
+ item = item.strip()
+ if '-' not in item:
+ return None
+ if item.startswith('-'):
+ if last_end < 0:
+ return None
+ begin = int(item)
+ end = None
+ last_end = -1
+ elif '-' in item:
+ begin, end = item.split('-', 1)
+ begin = int(begin)
+ if begin < last_end or last_end < 0:
+ return None
+ if end:
+ end = int(end) + 1
+ if begin >= end:
+ return None
+ else:
+ end = None
+ last_end = end
+ ranges.append((begin, end))
+
+ return Range(units, ranges)
+
+
+def parse_content_range_header(value, on_update=None):
+ """Parses a range header into a
+ :class:`~werkzeug.datastructures.ContentRange` object or `None` if
+ parsing is not possible.
+
+ .. versionadded:: 0.7
+
+ :param value: a content range header to be parsed.
+ :param on_update: an optional callable that is called every time a value
+ on the :class:`~werkzeug.datastructures.ContentRange`
+ object is changed.
+ """
+ if value is None:
+ return None
+ try:
+ units, rangedef = (value or '').strip().split(None, 1)
+ except ValueError:
+ return None
+
+ if '/' not in rangedef:
+ return None
+ rng, length = rangedef.split('/', 1)
+ if length == '*':
+ length = None
+ elif length.isdigit():
+ length = int(length)
+ else:
+ return None
+
+ if rng == '*':
+ return ContentRange(units, None, None, length, on_update=on_update)
+ elif '-' not in rng:
+ return None
+
+ start, stop = rng.split('-', 1)
+ try:
+ start = int(start)
+ stop = int(stop) + 1
+ except ValueError:
+ return None
+
+ if is_byte_range_valid(start, stop, length):
+ return ContentRange(units, start, stop, length, on_update=on_update)
+
+
+def quote_etag(etag, weak=False):
+ """Quote an etag.
+
+ :param etag: the etag to quote.
+ :param weak: set to `True` to tag it "weak".
+ """
+ if '"' in etag:
+ raise ValueError('invalid etag')
+ etag = '"%s"' % etag
+ if weak:
+ etag = 'w/' + etag
+ return etag
+
+
+def unquote_etag(etag):
+ """Unquote a single etag:
+
+ >>> unquote_etag('w/"bar"')
+ ('bar', True)
+ >>> unquote_etag('"bar"')
+ ('bar', False)
+
+ :param etag: the etag identifier to unquote.
+ :return: a ``(etag, weak)`` tuple.
+ """
+ if not etag:
+ return None, None
+ etag = etag.strip()
+ weak = False
+ if etag[:2] in ('w/', 'W/'):
+ weak = True
+ etag = etag[2:]
+ if etag[:1] == etag[-1:] == '"':
+ etag = etag[1:-1]
+ return etag, weak
+
+
+def parse_etags(value):
+ """Parse an etag header.
+
+ :param value: the tag header to parse
+ :return: an :class:`~werkzeug.datastructures.ETags` object.
+ """
+ if not value:
+ return ETags()
+ strong = []
+ weak = []
+ end = len(value)
+ pos = 0
+ while pos < end:
+ match = _etag_re.match(value, pos)
+ if match is None:
+ break
+ is_weak, quoted, raw = match.groups()
+ if raw == '*':
+ return ETags(star_tag=True)
+ elif quoted:
+ raw = quoted
+ if is_weak:
+ weak.append(raw)
+ else:
+ strong.append(raw)
+ pos = match.end()
+ return ETags(strong, weak)
+
+
+def generate_etag(data):
+ """Generate an etag for some data."""
+ return md5(data).hexdigest()
+
+
+def parse_date(value):
+ """Parse one of the following date formats into a datetime object:
+
+ .. sourcecode:: text
+
+ Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+
+ If parsing fails the return value is `None`.
+
+ :param value: a string with a supported date format.
+ :return: a :class:`datetime.datetime` object.
+ """
+ if value:
+ t = parsedate_tz(value.strip())
+ if t is not None:
+ try:
+ year = t[0]
+ # unfortunately that function does not tell us if two digit
+ # years were part of the string, or if they were prefixed
+ # with two zeroes. So what we do is to assume that 69-99
+ # refer to 1900, and everything below to 2000
+ if year >= 0 and year <= 68:
+ year += 2000
+ elif year >= 69 and year <= 99:
+ year += 1900
+ return datetime(*((year,) + t[1:7])) - \
+ timedelta(seconds=t[-1] or 0)
+ except (ValueError, OverflowError):
+ return None
+
+
+def cookie_date(expires=None):
+ """Formats the time to ensure compatibility with Netscape's cookie
+ standard.
+
+ Accepts a floating point number expressed in seconds since the epoch in, a
+ datetime object or a timetuple. All times in UTC. The :func:`parse_date`
+ function can be used to parse such a date.
+
+ Outputs a string in the format ``Wdy, DD-Mon-YYYY HH:MM:SS GMT``.
+
+ :param expires: If provided that date is used, otherwise the current.
+ """
+ return _dump_date(expires, '-')
+
+
+def http_date(timestamp=None):
+ """Formats the time to match the RFC1123 date format.
+
+ Accepts a floating point number expressed in seconds since the epoch in, a
+ datetime object or a timetuple. All times in UTC. The :func:`parse_date`
+ function can be used to parse such a date.
+
+ Outputs a string in the format ``Wdy, DD Mon YYYY HH:MM:SS GMT``.
+
+ :param timestamp: If provided that date is used, otherwise the current.
+ """
+ return _dump_date(timestamp, ' ')
+
+
+def is_resource_modified(environ, etag=None, data=None, last_modified=None):
+ """Convenience method for conditional requests.
+
+ :param environ: the WSGI environment of the request to be checked.
+ :param etag: the etag for the response for comparison.
+ :param data: or alternatively the data of the response to automatically
+ generate an etag using :func:`generate_etag`.
+ :param last_modified: an optional date of the last modification.
+ :return: `True` if the resource was modified, otherwise `False`.
+ """
+ if etag is None and data is not None:
+ etag = generate_etag(data)
+ elif data is not None:
+ raise TypeError('both data and etag given')
+ if environ['REQUEST_METHOD'] not in ('GET', 'HEAD'):
+ return False
+
+ unmodified = False
+ if isinstance(last_modified, basestring):
+ last_modified = parse_date(last_modified)
+
+ # ensure that microsecond is zero because the HTTP spec does not transmit
+ # that either and we might have some false positives. See issue #39
+ if last_modified is not None:
+ last_modified = last_modified.replace(microsecond=0)
+
+ modified_since = parse_date(environ.get('HTTP_IF_MODIFIED_SINCE'))
+
+ if modified_since and last_modified and last_modified <= modified_since:
+ unmodified = True
+ if etag:
+ if_none_match = parse_etags(environ.get('HTTP_IF_NONE_MATCH'))
+ if if_none_match:
+ unmodified = if_none_match.contains_raw(etag)
+
+ return not unmodified
+
+
+def remove_entity_headers(headers, allowed=('expires', 'content-location')):
+ """Remove all entity headers from a list or :class:`Headers` object. This
+ operation works in-place. `Expires` and `Content-Location` headers are
+ by default not removed. The reason for this is :rfc:`2616` section
+ 10.3.5 which specifies some entity headers that should be sent.
+
+ .. versionchanged:: 0.5
+ added `allowed` parameter.
+
+ :param headers: a list or :class:`Headers` object.
+ :param allowed: a list of headers that should still be allowed even though
+ they are entity headers.
+ """
+ allowed = set(x.lower() for x in allowed)
+ headers[:] = [(key, value) for key, value in headers if
+ not is_entity_header(key) or key.lower() in allowed]
+
+
+def remove_hop_by_hop_headers(headers):
+ """Remove all HTTP/1.1 "Hop-by-Hop" headers from a list or
+ :class:`Headers` object. This operation works in-place.
+
+ .. versionadded:: 0.5
+
+ :param headers: a list or :class:`Headers` object.
+ """
+ headers[:] = [(key, value) for key, value in headers if
+ not is_hop_by_hop_header(key)]
+
+
+def is_entity_header(header):
+ """Check if a header is an entity header.
+
+ .. versionadded:: 0.5
+
+ :param header: the header to test.
+ :return: `True` if it's an entity header, `False` otherwise.
+ """
+ return header.lower() in _entity_headers
+
+
+def is_hop_by_hop_header(header):
+ """Check if a header is an HTTP/1.1 "Hop-by-Hop" header.
+
+ .. versionadded:: 0.5
+
+ :param header: the header to test.
+ :return: `True` if it's an entity header, `False` otherwise.
+ """
+ return header.lower() in _hop_by_hop_headers
+
+
+def parse_cookie(header, charset='utf-8', errors='replace',
+ cls=None):
+ """Parse a cookie. Either from a string or WSGI environ.
+
+ Per default encoding errors are ignored. If you want a different behavior
+ you can set `errors` to ``'replace'`` or ``'strict'``. In strict mode a
+ :exc:`HTTPUnicodeError` is raised.
+
+ .. versionchanged:: 0.5
+ This function now returns a :class:`TypeConversionDict` instead of a
+ regular dict. The `cls` parameter was added.
+
+ :param header: the header to be used to parse the cookie. Alternatively
+ this can be a WSGI environment.
+ :param charset: the charset for the cookie values.
+ :param errors: the error behavior for the charset decoding.
+ :param cls: an optional dict class to use. If this is not specified
+ or `None` the default :class:`TypeConversionDict` is
+ used.
+ """
+ if isinstance(header, dict):
+ header = header.get('HTTP_COOKIE', '')
+ if cls is None:
+ cls = TypeConversionDict
+ cookie = _ExtendedCookie()
+ cookie.load(header)
+ result = {}
+
+ # decode to unicode and skip broken items. Our extended morsel
+ # and extended cookie will catch CookieErrors and convert them to
+ # `None` items which we have to skip here.
+ for key, value in cookie.iteritems():
+ if value.value is not None:
+ result[key] = _decode_unicode(unquote_header_value(value.value),
+ charset, errors)
+
+ return cls(result)
+
+
+def dump_cookie(key, value='', max_age=None, expires=None, path='/',
+ domain=None, secure=None, httponly=False, charset='utf-8',
+ sync_expires=True):
+ """Creates a new Set-Cookie header without the ``Set-Cookie`` prefix
+ The parameters are the same as in the cookie Morsel object in the
+ Python standard library but it accepts unicode data, too.
+
+ :param max_age: should be a number of seconds, or `None` (default) if
+ the cookie should last only as long as the client's
+ browser session. Additionally `timedelta` objects
+ are accepted, too.
+ :param expires: should be a `datetime` object or unix timestamp.
+ :param path: limits the cookie to a given path, per default it will
+ span the whole domain.
+ :param domain: Use this if you want to set a cross-domain cookie. For
+ example, ``domain=".example.com"`` will set a cookie
+ that is readable by the domain ``www.example.com``,
+ ``foo.example.com`` etc. Otherwise, a cookie will only
+ be readable by the domain that set it.
+ :param secure: The cookie will only be available via HTTPS
+ :param httponly: disallow JavaScript to access the cookie. This is an
+ extension to the cookie standard and probably not
+ supported by all browsers.
+ :param charset: the encoding for unicode values.
+ :param sync_expires: automatically set expires if max_age is defined
+ but expires not.
+ """
+ try:
+ key = str(key)
+ except UnicodeError:
+ raise TypeError('invalid key %r' % key)
+ if isinstance(value, unicode):
+ value = value.encode(charset)
+ value = quote_header_value(value)
+ morsel = _ExtendedMorsel(key, value)
+ if isinstance(max_age, timedelta):
+ max_age = (max_age.days * 60 * 60 * 24) + max_age.seconds
+ if expires is not None:
+ if not isinstance(expires, basestring):
+ expires = cookie_date(expires)
+ morsel['expires'] = expires
+ elif max_age is not None and sync_expires:
+ morsel['expires'] = cookie_date(time() + max_age)
+ if domain and ':' in domain:
+ # The port part of the domain should NOT be used. Strip it
+ domain = domain.split(':', 1)[0]
+ if domain:
+ assert '.' in domain, (
+ "Setting \"domain\" for a cookie on a server running localy (ex: "
+ "localhost) is not supportted by complying browsers. You should "
+ "have something like: \"127.0.0.1 localhost dev.localhost\" on "
+ "your hosts file and then point your server to run on "
+ "\"dev.localhost\" and also set \"domain\" for \"dev.localhost\""
+ )
+ for k, v in (('path', path), ('domain', domain), ('secure', secure),
+ ('max-age', max_age), ('httponly', httponly)):
+ if v is not None and v is not False:
+ morsel[k] = str(v)
+ return morsel.output(header='').lstrip()
+
+
+def is_byte_range_valid(start, stop, length):
+ """Checks if a given byte content range is valid for the given length.
+
+ .. versionadded:: 0.7
+ """
+ if (start is None) != (stop is None):
+ return False
+ elif start is None:
+ return length is None or length >= 0
+ elif length is None:
+ return 0 <= start < stop
+ elif start >= stop:
+ return False
+ return 0 <= start < length
+
+
+# circular dependency fun
+from werkzeug.datastructures import Accept, HeaderSet, ETags, Authorization, \
+ WWWAuthenticate, TypeConversionDict, IfRange, Range, ContentRange, \
+ RequestCacheControl
+
+
+# DEPRECATED
+# backwards compatible imports
+from werkzeug.datastructures import MIMEAccept, CharsetAccept, \
+ LanguageAccept, Headers
diff --git a/websdk/werkzeug/local.py b/websdk/werkzeug/local.py
new file mode 100644
index 0000000..c5507bb
--- /dev/null
+++ b/websdk/werkzeug/local.py
@@ -0,0 +1,394 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.local
+ ~~~~~~~~~~~~~~
+
+ This module implements context-local objects.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+from werkzeug.wsgi import ClosingIterator
+from werkzeug._internal import _patch_wrapper
+
+# since each thread has its own greenlet we can just use those as identifiers
+# for the context. If greenlets are not available we fall back to the
+# current thread ident.
+try:
+ from greenlet import getcurrent as get_ident
+except ImportError: # pragma: no cover
+ try:
+ from thread import get_ident
+ except ImportError: # pragma: no cover
+ from dummy_thread import get_ident
+
+
+def release_local(local):
+ """Releases the contents of the local for the current context.
+ This makes it possible to use locals without a manager.
+
+ Example::
+
+ >>> loc = Local()
+ >>> loc.foo = 42
+ >>> release_local(loc)
+ >>> hasattr(loc, 'foo')
+ False
+
+ With this function one can release :class:`Local` objects as well
+ as :class:`StackLocal` objects. However it is not possible to
+ release data held by proxies that way, one always has to retain
+ a reference to the underlying local object in order to be able
+ to release it.
+
+ .. versionadded:: 0.6.1
+ """
+ local.__release_local__()
+
+
+class Local(object):
+ __slots__ = ('__storage__', '__ident_func__')
+
+ def __init__(self):
+ object.__setattr__(self, '__storage__', {})
+ object.__setattr__(self, '__ident_func__', get_ident)
+
+ def __iter__(self):
+ return iter(self.__storage__.items())
+
+ def __call__(self, proxy):
+ """Create a proxy for a name."""
+ return LocalProxy(self, proxy)
+
+ def __release_local__(self):
+ self.__storage__.pop(self.__ident_func__(), None)
+
+ def __getattr__(self, name):
+ try:
+ return self.__storage__[self.__ident_func__()][name]
+ except KeyError:
+ raise AttributeError(name)
+
+ def __setattr__(self, name, value):
+ ident = self.__ident_func__()
+ storage = self.__storage__
+ try:
+ storage[ident][name] = value
+ except KeyError:
+ storage[ident] = {name: value}
+
+ def __delattr__(self, name):
+ try:
+ del self.__storage__[self.__ident_func__()][name]
+ except KeyError:
+ raise AttributeError(name)
+
+
+class LocalStack(object):
+ """This class works similar to a :class:`Local` but keeps a stack
+ of objects instead. This is best explained with an example::
+
+ >>> ls = LocalStack()
+ >>> ls.push(42)
+ >>> ls.top
+ 42
+ >>> ls.push(23)
+ >>> ls.top
+ 23
+ >>> ls.pop()
+ 23
+ >>> ls.top
+ 42
+
+ They can be force released by using a :class:`LocalManager` or with
+ the :func:`release_local` function but the correct way is to pop the
+ item from the stack after using. When the stack is empty it will
+ no longer be bound to the current context (and as such released).
+
+ By calling the stack without arguments it returns a proxy that resolves to
+ the topmost item on the stack.
+
+ .. versionadded:: 0.6.1
+ """
+
+ def __init__(self):
+ self._local = Local()
+
+ def __release_local__(self):
+ self._local.__release_local__()
+
+ def _get__ident_func__(self):
+ return self._local.__ident_func__
+ def _set__ident_func__(self, value):
+ object.__setattr__(self._local, '__ident_func__', value)
+ __ident_func__ = property(_get__ident_func__, _set__ident_func__)
+ del _get__ident_func__, _set__ident_func__
+
+ def __call__(self):
+ def _lookup():
+ rv = self.top
+ if rv is None:
+ raise RuntimeError('object unbound')
+ return rv
+ return LocalProxy(_lookup)
+
+ def push(self, obj):
+ """Pushes a new item to the stack"""
+ rv = getattr(self._local, 'stack', None)
+ if rv is None:
+ self._local.stack = rv = []
+ rv.append(obj)
+ return rv
+
+ def pop(self):
+ """Removes the topmost item from the stack, will return the
+ old value or `None` if the stack was already empty.
+ """
+ stack = getattr(self._local, 'stack', None)
+ if stack is None:
+ return None
+ elif len(stack) == 1:
+ release_local(self._local)
+ return stack[-1]
+ else:
+ return stack.pop()
+
+ @property
+ def top(self):
+ """The topmost item on the stack. If the stack is empty,
+ `None` is returned.
+ """
+ try:
+ return self._local.stack[-1]
+ except (AttributeError, IndexError):
+ return None
+
+
+class LocalManager(object):
+ """Local objects cannot manage themselves. For that you need a local
+ manager. You can pass a local manager multiple locals or add them later
+ by appending them to `manager.locals`. Everytime the manager cleans up
+ it, will clean up all the data left in the locals for this context.
+
+ The `ident_func` parameter can be added to override the default ident
+ function for the wrapped locals.
+
+ .. versionchanged:: 0.6.1
+ Instead of a manager the :func:`release_local` function can be used
+ as well.
+
+ .. versionchanged:: 0.7
+ `ident_func` was added.
+ """
+
+ def __init__(self, locals=None, ident_func=None):
+ if locals is None:
+ self.locals = []
+ elif isinstance(locals, Local):
+ self.locals = [locals]
+ else:
+ self.locals = list(locals)
+ if ident_func is not None:
+ self.ident_func = ident_func
+ for local in self.locals:
+ object.__setattr__(local, '__ident_func__', ident_func)
+ else:
+ self.ident_func = get_ident
+
+ def get_ident(self):
+ """Return the context identifier the local objects use internally for
+ this context. You cannot override this method to change the behavior
+ but use it to link other context local objects (such as SQLAlchemy's
+ scoped sessions) to the Werkzeug locals.
+
+ .. versionchanged:: 0.7
+ Yu can pass a different ident function to the local manager that
+ will then be propagated to all the locals passed to the
+ constructor.
+ """
+ return self.ident_func()
+
+ def cleanup(self):
+ """Manually clean up the data in the locals for this context. Call
+ this at the end of the request or use `make_middleware()`.
+ """
+ for local in self.locals:
+ release_local(local)
+
+ def make_middleware(self, app):
+ """Wrap a WSGI application so that cleaning up happens after
+ request end.
+ """
+ def application(environ, start_response):
+ return ClosingIterator(app(environ, start_response), self.cleanup)
+ return application
+
+ def middleware(self, func):
+ """Like `make_middleware` but for decorating functions.
+
+ Example usage::
+
+ @manager.middleware
+ def application(environ, start_response):
+ ...
+
+ The difference to `make_middleware` is that the function passed
+ will have all the arguments copied from the inner application
+ (name, docstring, module).
+ """
+ return _patch_wrapper(func, self.make_middleware(func))
+
+ def __repr__(self):
+ return '<%s storages: %d>' % (
+ self.__class__.__name__,
+ len(self.locals)
+ )
+
+
+class LocalProxy(object):
+ """Acts as a proxy for a werkzeug local. Forwards all operations to
+ a proxied object. The only operations not supported for forwarding
+ are right handed operands and any kind of assignment.
+
+ Example usage::
+
+ from werkzeug.local import Local
+ l = Local()
+
+ # these are proxies
+ request = l('request')
+ user = l('user')
+
+
+ from werkzeug.local import LocalStack
+ _response_local = LocalStack()
+
+ # this is a proxy
+ response = _response_local()
+
+ Whenever something is bound to l.user / l.request the proxy objects
+ will forward all operations. If no object is bound a :exc:`RuntimeError`
+ will be raised.
+
+ To create proxies to :class:`Local` or :class:`LocalStack` objects,
+ call the object as shown above. If you want to have a proxy to an
+ object looked up by a function, you can (as of Werkzeug 0.6.1) pass
+ a function to the :class:`LocalProxy` constructor::
+
+ session = LocalProxy(lambda: get_current_request().session)
+
+ .. versionchanged:: 0.6.1
+ The class can be instanciated with a callable as well now.
+ """
+ __slots__ = ('__local', '__dict__', '__name__')
+
+ def __init__(self, local, name=None):
+ object.__setattr__(self, '_LocalProxy__local', local)
+ object.__setattr__(self, '__name__', name)
+
+ def _get_current_object(self):
+ """Return the current object. This is useful if you want the real
+ object behind the proxy at a time for performance reasons or because
+ you want to pass the object into a different context.
+ """
+ if not hasattr(self.__local, '__release_local__'):
+ return self.__local()
+ try:
+ return getattr(self.__local, self.__name__)
+ except AttributeError:
+ raise RuntimeError('no object bound to %s' % self.__name__)
+
+ @property
+ def __dict__(self):
+ try:
+ return self._get_current_object().__dict__
+ except RuntimeError:
+ raise AttributeError('__dict__')
+
+ def __repr__(self):
+ try:
+ obj = self._get_current_object()
+ except RuntimeError:
+ return '<%s unbound>' % self.__class__.__name__
+ return repr(obj)
+
+ def __nonzero__(self):
+ try:
+ return bool(self._get_current_object())
+ except RuntimeError:
+ return False
+
+ def __unicode__(self):
+ try:
+ return unicode(self._get_current_object())
+ except RuntimeError:
+ return repr(self)
+
+ def __dir__(self):
+ try:
+ return dir(self._get_current_object())
+ except RuntimeError:
+ return []
+
+ def __getattr__(self, name):
+ if name == '__members__':
+ return dir(self._get_current_object())
+ return getattr(self._get_current_object(), name)
+
+ def __setitem__(self, key, value):
+ self._get_current_object()[key] = value
+
+ def __delitem__(self, key):
+ del self._get_current_object()[key]
+
+ def __setslice__(self, i, j, seq):
+ self._get_current_object()[i:j] = seq
+
+ def __delslice__(self, i, j):
+ del self._get_current_object()[i:j]
+
+ __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
+ __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
+ __str__ = lambda x: str(x._get_current_object())
+ __lt__ = lambda x, o: x._get_current_object() < o
+ __le__ = lambda x, o: x._get_current_object() <= o
+ __eq__ = lambda x, o: x._get_current_object() == o
+ __ne__ = lambda x, o: x._get_current_object() != o
+ __gt__ = lambda x, o: x._get_current_object() > o
+ __ge__ = lambda x, o: x._get_current_object() >= o
+ __cmp__ = lambda x, o: cmp(x._get_current_object(), o)
+ __hash__ = lambda x: hash(x._get_current_object())
+ __call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw)
+ __len__ = lambda x: len(x._get_current_object())
+ __getitem__ = lambda x, i: x._get_current_object()[i]
+ __iter__ = lambda x: iter(x._get_current_object())
+ __contains__ = lambda x, i: i in x._get_current_object()
+ __getslice__ = lambda x, i, j: x._get_current_object()[i:j]
+ __add__ = lambda x, o: x._get_current_object() + o
+ __sub__ = lambda x, o: x._get_current_object() - o
+ __mul__ = lambda x, o: x._get_current_object() * o
+ __floordiv__ = lambda x, o: x._get_current_object() // o
+ __mod__ = lambda x, o: x._get_current_object() % o
+ __divmod__ = lambda x, o: x._get_current_object().__divmod__(o)
+ __pow__ = lambda x, o: x._get_current_object() ** o
+ __lshift__ = lambda x, o: x._get_current_object() << o
+ __rshift__ = lambda x, o: x._get_current_object() >> o
+ __and__ = lambda x, o: x._get_current_object() & o
+ __xor__ = lambda x, o: x._get_current_object() ^ o
+ __or__ = lambda x, o: x._get_current_object() | o
+ __div__ = lambda x, o: x._get_current_object().__div__(o)
+ __truediv__ = lambda x, o: x._get_current_object().__truediv__(o)
+ __neg__ = lambda x: -(x._get_current_object())
+ __pos__ = lambda x: +(x._get_current_object())
+ __abs__ = lambda x: abs(x._get_current_object())
+ __invert__ = lambda x: ~(x._get_current_object())
+ __complex__ = lambda x: complex(x._get_current_object())
+ __int__ = lambda x: int(x._get_current_object())
+ __long__ = lambda x: long(x._get_current_object())
+ __float__ = lambda x: float(x._get_current_object())
+ __oct__ = lambda x: oct(x._get_current_object())
+ __hex__ = lambda x: hex(x._get_current_object())
+ __index__ = lambda x: x._get_current_object().__index__()
+ __coerce__ = lambda x, o: x.__coerce__(x, o)
+ __enter__ = lambda x: x.__enter__()
+ __exit__ = lambda x, *a, **kw: x.__exit__(*a, **kw)
diff --git a/websdk/werkzeug/posixemulation.py b/websdk/werkzeug/posixemulation.py
new file mode 100644
index 0000000..d131d23
--- /dev/null
+++ b/websdk/werkzeug/posixemulation.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+r"""
+ werkzeug.posixemulation
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Provides a POSIX emulation for some features that are relevant to
+ web applications. The main purpose is to simplify support for
+ systems such as Windows NT that are not 100% POSIX compatible.
+
+ Currently this only implements a :func:`rename` function that
+ follows POSIX semantics. Eg: if the target file already exists it
+ will be replaced without asking.
+
+ This module was introduced in 0.6.1 and is not a public interface.
+ It might become one in later versions of Werkzeug.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+import os
+import errno
+import time
+import random
+
+
+can_rename_open_file = False
+if os.name == 'nt': # pragma: no cover
+ _rename = lambda src, dst: False
+ _rename_atomic = lambda src, dst: False
+
+ try:
+ import ctypes
+
+ _MOVEFILE_REPLACE_EXISTING = 0x1
+ _MOVEFILE_WRITE_THROUGH = 0x8
+ _MoveFileEx = ctypes.windll.kernel32.MoveFileExW
+
+ def _rename(src, dst):
+ if not isinstance(src, unicode):
+ src = unicode(src, sys.getfilesystemencoding())
+ if not isinstance(dst, unicode):
+ dst = unicode(dst, sys.getfilesystemencoding())
+ if _rename_atomic(src, dst):
+ return True
+ retry = 0
+ rv = False
+ while not rv and retry < 100:
+ rv = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING |
+ _MOVEFILE_WRITE_THROUGH)
+ if not rv:
+ time.sleep(0.001)
+ retry += 1
+ return rv
+
+ # new in Vista and Windows Server 2008
+ _CreateTransaction = ctypes.windll.ktmw32.CreateTransaction
+ _CommitTransaction = ctypes.windll.ktmw32.CommitTransaction
+ _MoveFileTransacted = ctypes.windll.kernel32.MoveFileTransactedW
+ _CloseHandle = ctypes.windll.kernel32.CloseHandle
+ can_rename_open_file = True
+
+ def _rename_atomic(src, dst):
+ ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Werkzeug rename')
+ if ta == -1:
+ return False
+ try:
+ retry = 0
+ rv = False
+ while not rv and retry < 100:
+ rv = _MoveFileTransacted(src, dst, None, None,
+ _MOVEFILE_REPLACE_EXISTING |
+ _MOVEFILE_WRITE_THROUGH, ta)
+ if rv:
+ rv = _CommitTransaction(ta)
+ break
+ else:
+ time.sleep(0.001)
+ retry += 1
+ return rv
+ finally:
+ _CloseHandle(ta)
+ except Exception:
+ pass
+
+ def rename(src, dst):
+ # Try atomic or pseudo-atomic rename
+ if _rename(src, dst):
+ return
+ # Fall back to "move away and replace"
+ try:
+ os.rename(src, dst)
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise
+ old = "%s-%08x" % (dst, random.randint(0, sys.maxint))
+ os.rename(dst, old)
+ os.rename(src, dst)
+ try:
+ os.unlink(old)
+ except Exception:
+ pass
+else:
+ rename = os.rename
+ can_rename_open_file = True
diff --git a/websdk/werkzeug/routing.py b/websdk/werkzeug/routing.py
new file mode 100644
index 0000000..9f8ca01
--- /dev/null
+++ b/websdk/werkzeug/routing.py
@@ -0,0 +1,1620 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.routing
+ ~~~~~~~~~~~~~~~~
+
+ When it comes to combining multiple controller or view functions (however
+ you want to call them) you need a dispatcher. A simple way would be
+ applying regular expression tests on the ``PATH_INFO`` and calling
+ registered callback functions that return the value then.
+
+ This module implements a much more powerful system than simple regular
+ expression matching because it can also convert values in the URLs and
+ build URLs.
+
+ Here a simple example that creates an URL map for an application with
+ two subdomains (www and kb) and some URL rules:
+
+ >>> m = Map([
+ ... # Static URLs
+ ... Rule('/', endpoint='static/index'),
+ ... Rule('/about', endpoint='static/about'),
+ ... Rule('/help', endpoint='static/help'),
+ ... # Knowledge Base
+ ... Subdomain('kb', [
+ ... Rule('/', endpoint='kb/index'),
+ ... Rule('/browse/', endpoint='kb/browse'),
+ ... Rule('/browse/<int:id>/', endpoint='kb/browse'),
+ ... Rule('/browse/<int:id>/<int:page>', endpoint='kb/browse')
+ ... ])
+ ... ], default_subdomain='www')
+
+ If the application doesn't use subdomains it's perfectly fine to not set
+ the default subdomain and not use the `Subdomain` rule factory. The endpoint
+ in the rules can be anything, for example import paths or unique
+ identifiers. The WSGI application can use those endpoints to get the
+ handler for that URL. It doesn't have to be a string at all but it's
+ recommended.
+
+ Now it's possible to create a URL adapter for one of the subdomains and
+ build URLs:
+
+ >>> c = m.bind('example.com')
+ >>> c.build("kb/browse", dict(id=42))
+ 'http://kb.example.com/browse/42/'
+ >>> c.build("kb/browse", dict())
+ 'http://kb.example.com/browse/'
+ >>> c.build("kb/browse", dict(id=42, page=3))
+ 'http://kb.example.com/browse/42/3'
+ >>> c.build("static/about")
+ '/about'
+ >>> c.build("static/index", force_external=True)
+ 'http://www.example.com/'
+
+ >>> c = m.bind('example.com', subdomain='kb')
+ >>> c.build("static/about")
+ 'http://www.example.com/about'
+
+ The first argument to bind is the server name *without* the subdomain.
+ Per default it will assume that the script is mounted on the root, but
+ often that's not the case so you can provide the real mount point as
+ second argument:
+
+ >>> c = m.bind('example.com', '/applications/example')
+
+ The third argument can be the subdomain, if not given the default
+ subdomain is used. For more details about binding have a look at the
+ documentation of the `MapAdapter`.
+
+ And here is how you can match URLs:
+
+ >>> c = m.bind('example.com')
+ >>> c.match("/")
+ ('static/index', {})
+ >>> c.match("/about")
+ ('static/about', {})
+ >>> c = m.bind('example.com', '/', 'kb')
+ >>> c.match("/")
+ ('kb/index', {})
+ >>> c.match("/browse/42/23")
+ ('kb/browse', {'id': 42, 'page': 23})
+
+ If matching fails you get a `NotFound` exception, if the rule thinks
+ it's a good idea to redirect (for example because the URL was defined
+ to have a slash at the end but the request was missing that slash) it
+ will raise a `RequestRedirect` exception. Both are subclasses of the
+ `HTTPException` so you can use those errors as responses in the
+ application.
+
+ If matching succeeded but the URL rule was incompatible to the given
+ method (for example there were only rules for `GET` and `HEAD` and
+ routing system tried to match a `POST` request) a `MethodNotAllowed`
+ method is raised.
+
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+import posixpath
+from pprint import pformat
+from urlparse import urljoin
+
+from werkzeug.urls import url_encode, url_decode, url_quote
+from werkzeug.utils import redirect, format_string
+from werkzeug.exceptions import HTTPException, NotFound, MethodNotAllowed
+from werkzeug._internal import _get_environ
+from werkzeug.datastructures import ImmutableDict, MultiDict
+
+
+_rule_re = re.compile(r'''
+ (?P<static>[^<]*) # static rule data
+ <
+ (?:
+ (?P<converter>[a-zA-Z_][a-zA-Z0-9_]*) # converter name
+ (?:\((?P<args>.*?)\))? # converter arguments
+ \: # variable delimiter
+ )?
+ (?P<variable>[a-zA-Z_][a-zA-Z0-9_]*) # variable name
+ >
+''', re.VERBOSE)
+_simple_rule_re = re.compile(r'<([^>]+)>')
+_converter_args_re = re.compile(r'''
+ ((?P<name>\w+)\s*=\s*)?
+ (?P<value>
+ True|False|
+ \d+.\d+|
+ \d+.|
+ \d+|
+ \w+|
+ [urUR]?(?P<stringval>"[^"]*?"|'[^']*')
+ )\s*,
+''', re.VERBOSE|re.UNICODE)
+
+
+_PYTHON_CONSTANTS = {
+ 'None': None,
+ 'True': True,
+ 'False': False
+}
+
+
+def _pythonize(value):
+ if value in _PYTHON_CONSTANTS:
+ return _PYTHON_CONSTANTS[value]
+ for convert in int, float:
+ try:
+ return convert(value)
+ except ValueError:
+ pass
+ if value[:1] == value[-1:] and value[0] in '"\'':
+ value = value[1:-1]
+ return unicode(value)
+
+
+def parse_converter_args(argstr):
+ argstr += ','
+ args = []
+ kwargs = {}
+
+ for item in _converter_args_re.finditer(argstr):
+ value = item.group('stringval')
+ if value is None:
+ value = item.group('value')
+ value = _pythonize(value)
+ if not item.group('name'):
+ args.append(value)
+ else:
+ name = item.group('name')
+ kwargs[name] = value
+
+ return tuple(args), kwargs
+
+
+def parse_rule(rule):
+ """Parse a rule and return it as generator. Each iteration yields tuples
+ in the form ``(converter, arguments, variable)``. If the converter is
+ `None` it's a static url part, otherwise it's a dynamic one.
+
+ :internal:
+ """
+ pos = 0
+ end = len(rule)
+ do_match = _rule_re.match
+ used_names = set()
+ while pos < end:
+ m = do_match(rule, pos)
+ if m is None:
+ break
+ data = m.groupdict()
+ if data['static']:
+ yield None, None, data['static']
+ variable = data['variable']
+ converter = data['converter'] or 'default'
+ if variable in used_names:
+ raise ValueError('variable name %r used twice.' % variable)
+ used_names.add(variable)
+ yield converter, data['args'] or None, variable
+ pos = m.end()
+ if pos < end:
+ remaining = rule[pos:]
+ if '>' in remaining or '<' in remaining:
+ raise ValueError('malformed url rule: %r' % rule)
+ yield None, None, remaining
+
+
+def get_converter(map, name, args):
+ """Create a new converter for the given arguments or raise
+ exception if the converter does not exist.
+
+ :internal:
+ """
+ if not name in map.converters:
+ raise LookupError('the converter %r does not exist' % name)
+ if args:
+ args, kwargs = parse_converter_args(args)
+ else:
+ args = ()
+ kwargs = {}
+ return map.converters[name](map, *args, **kwargs)
+
+
+class RoutingException(Exception):
+ """Special exceptions that require the application to redirect, notifying
+ about missing urls, etc.
+
+ :internal:
+ """
+
+
+class RequestRedirect(HTTPException, RoutingException):
+ """Raise if the map requests a redirect. This is for example the case if
+ `strict_slashes` are activated and an url that requires a trailing slash.
+
+ The attribute `new_url` contains the absolute destination url.
+ """
+ code = 301
+
+ def __init__(self, new_url):
+ RoutingException.__init__(self, new_url)
+ self.new_url = new_url
+
+ def get_response(self, environ):
+ return redirect(self.new_url, 301)
+
+
+class RequestSlash(RoutingException):
+ """Internal exception."""
+
+
+class RequestAliasRedirect(RoutingException):
+ """This rule is an alias and wants to redirect to the canonical URL."""
+
+ def __init__(self, matched_values):
+ self.matched_values = matched_values
+
+
+class BuildError(RoutingException, LookupError):
+ """Raised if the build system cannot find a URL for an endpoint with the
+ values provided.
+ """
+
+ def __init__(self, endpoint, values, method):
+ LookupError.__init__(self, endpoint, values, method)
+ self.endpoint = endpoint
+ self.values = values
+ self.method = method
+
+
+class ValidationError(ValueError):
+ """Validation error. If a rule converter raises this exception the rule
+ does not match the current URL and the next URL is tried.
+ """
+
+
+class RuleFactory(object):
+ """As soon as you have more complex URL setups it's a good idea to use rule
+ factories to avoid repetitive tasks. Some of them are builtin, others can
+ be added by subclassing `RuleFactory` and overriding `get_rules`.
+ """
+
+ def get_rules(self, map):
+ """Subclasses of `RuleFactory` have to override this method and return
+ an iterable of rules."""
+ raise NotImplementedError()
+
+
+class Subdomain(RuleFactory):
+ """All URLs provided by this factory have the subdomain set to a
+ specific domain. For example if you want to use the subdomain for
+ the current language this can be a good setup::
+
+ url_map = Map([
+ Rule('/', endpoint='#select_language'),
+ Subdomain('<string(length=2):lang_code>', [
+ Rule('/', endpoint='index'),
+ Rule('/about', endpoint='about'),
+ Rule('/help', endpoint='help')
+ ])
+ ])
+
+ All the rules except for the ``'#select_language'`` endpoint will now
+ listen on a two letter long subdomain that holds the language code
+ for the current request.
+ """
+
+ def __init__(self, subdomain, rules):
+ self.subdomain = subdomain
+ self.rules = rules
+
+ def get_rules(self, map):
+ for rulefactory in self.rules:
+ for rule in rulefactory.get_rules(map):
+ rule = rule.empty()
+ rule.subdomain = self.subdomain
+ yield rule
+
+
+class Submount(RuleFactory):
+ """Like `Subdomain` but prefixes the URL rule with a given string::
+
+ url_map = Map([
+ Rule('/', endpoint='index'),
+ Submount('/blog', [
+ Rule('/', endpoint='blog/index'),
+ Rule('/entry/<entry_slug>', endpoint='blog/show')
+ ])
+ ])
+
+ Now the rule ``'blog/show'`` matches ``/blog/entry/<entry_slug>``.
+ """
+
+ def __init__(self, path, rules):
+ self.path = path.rstrip('/')
+ self.rules = rules
+
+ def get_rules(self, map):
+ for rulefactory in self.rules:
+ for rule in rulefactory.get_rules(map):
+ rule = rule.empty()
+ rule.rule = self.path + rule.rule
+ yield rule
+
+
+class EndpointPrefix(RuleFactory):
+ """Prefixes all endpoints (which must be strings for this factory) with
+ another string. This can be useful for sub applications::
+
+ url_map = Map([
+ Rule('/', endpoint='index'),
+ EndpointPrefix('blog/', [Submount('/blog', [
+ Rule('/', endpoint='index'),
+ Rule('/entry/<entry_slug>', endpoint='show')
+ ])])
+ ])
+ """
+
+ def __init__(self, prefix, rules):
+ self.prefix = prefix
+ self.rules = rules
+
+ def get_rules(self, map):
+ for rulefactory in self.rules:
+ for rule in rulefactory.get_rules(map):
+ rule = rule.empty()
+ rule.endpoint = self.prefix + rule.endpoint
+ yield rule
+
+
+class RuleTemplate(object):
+ """Returns copies of the rules wrapped and expands string templates in
+ the endpoint, rule, defaults or subdomain sections.
+
+ Here a small example for such a rule template::
+
+ from werkzeug.routing import Map, Rule, RuleTemplate
+
+ resource = RuleTemplate([
+ Rule('/$name/', endpoint='$name.list'),
+ Rule('/$name/<int:id>', endpoint='$name.show')
+ ])
+
+ url_map = Map([resource(name='user'), resource(name='page')])
+
+ When a rule template is called the keyword arguments are used to
+ replace the placeholders in all the string parameters.
+ """
+
+ def __init__(self, rules):
+ self.rules = list(rules)
+
+ def __call__(self, *args, **kwargs):
+ return RuleTemplateFactory(self.rules, dict(*args, **kwargs))
+
+
+class RuleTemplateFactory(RuleFactory):
+ """A factory that fills in template variables into rules. Used by
+ `RuleTemplate` internally.
+
+ :internal:
+ """
+
+ def __init__(self, rules, context):
+ self.rules = rules
+ self.context = context
+
+ def get_rules(self, map):
+ for rulefactory in self.rules:
+ for rule in rulefactory.get_rules(map):
+ new_defaults = subdomain = None
+ if rule.defaults:
+ new_defaults = {}
+ for key, value in rule.defaults.iteritems():
+ if isinstance(value, basestring):
+ value = format_string(value, self.context)
+ new_defaults[key] = value
+ if rule.subdomain is not None:
+ subdomain = format_string(rule.subdomain, self.context)
+ new_endpoint = rule.endpoint
+ if isinstance(new_endpoint, basestring):
+ new_endpoint = format_string(new_endpoint, self.context)
+ yield Rule(
+ format_string(rule.rule, self.context),
+ new_defaults,
+ subdomain,
+ rule.methods,
+ rule.build_only,
+ new_endpoint,
+ rule.strict_slashes
+ )
+
+
+class Rule(RuleFactory):
+ """A Rule represents one URL pattern. There are some options for `Rule`
+ that change the way it behaves and are passed to the `Rule` constructor.
+ Note that besides the rule-string all arguments *must* be keyword arguments
+ in order to not break the application on Werkzeug upgrades.
+
+ `string`
+ Rule strings basically are just normal URL paths with placeholders in
+ the format ``<converter(arguments):name>`` where the converter and the
+ arguments are optional. If no converter is defined the `default`
+ converter is used which means `string` in the normal configuration.
+
+ URL rules that end with a slash are branch URLs, others are leaves.
+ If you have `strict_slashes` enabled (which is the default), all
+ branch URLs that are matched without a trailing slash will trigger a
+ redirect to the same URL with the missing slash appended.
+
+ The converters are defined on the `Map`.
+
+ `endpoint`
+ The endpoint for this rule. This can be anything. A reference to a
+ function, a string, a number etc. The preferred way is using a string
+ because the endpoint is used for URL generation.
+
+ `defaults`
+ An optional dict with defaults for other rules with the same endpoint.
+ This is a bit tricky but useful if you want to have unique URLs::
+
+ url_map = Map([
+ Rule('/all/', defaults={'page': 1}, endpoint='all_entries'),
+ Rule('/all/page/<int:page>', endpoint='all_entries')
+ ])
+
+ If a user now visits ``http://example.com/all/page/1`` he will be
+ redirected to ``http://example.com/all/``. If `redirect_defaults` is
+ disabled on the `Map` instance this will only affect the URL
+ generation.
+
+ `subdomain`
+ The subdomain rule string for this rule. If not specified the rule
+ only matches for the `default_subdomain` of the map. If the map is
+ not bound to a subdomain this feature is disabled.
+
+ Can be useful if you want to have user profiles on different subdomains
+ and all subdomains are forwarded to your application::
+
+ url_map = Map([
+ Rule('/', subdomain='<username>', endpoint='user/homepage'),
+ Rule('/stats', subdomain='<username>', endpoint='user/stats')
+ ])
+
+ `methods`
+ A sequence of http methods this rule applies to. If not specified, all
+ methods are allowed. For example this can be useful if you want different
+ endpoints for `POST` and `GET`. If methods are defined and the path
+ matches but the method matched against is not in this list or in the
+ list of another rule for that path the error raised is of the type
+ `MethodNotAllowed` rather than `NotFound`. If `GET` is present in the
+ list of methods and `HEAD` is not, `HEAD` is added automatically.
+
+ .. versionchanged:: 0.6.1
+ `HEAD` is now automatically added to the methods if `GET` is
+ present. The reason for this is that existing code often did not
+ work properly in servers not rewriting `HEAD` to `GET`
+ automatically and it was not documented how `HEAD` should be
+ treated. This was considered a bug in Werkzeug because of that.
+
+ `strict_slashes`
+ Override the `Map` setting for `strict_slashes` only for this rule. If
+ not specified the `Map` setting is used.
+
+ `build_only`
+ Set this to True and the rule will never match but will create a URL
+ that can be build. This is useful if you have resources on a subdomain
+ or folder that are not handled by the WSGI application (like static data)
+
+ `redirect_to`
+ If given this must be either a string or callable. In case of a
+ callable it's called with the url adapter that triggered the match and
+ the values of the URL as keyword arguments and has to return the target
+ for the redirect, otherwise it has to be a string with placeholders in
+ rule syntax::
+
+ def foo_with_slug(adapter, id):
+ # ask the database for the slug for the old id. this of
+ # course has nothing to do with werkzeug.
+ return 'foo/' + Foo.get_slug_for_id(id)
+
+ url_map = Map([
+ Rule('/foo/<slug>', endpoint='foo'),
+ Rule('/some/old/url/<slug>', redirect_to='foo/<slug>'),
+ Rule('/other/old/url/<int:id>', redirect_to=foo_with_slug)
+ ])
+
+ When the rule is matched the routing system will raise a
+ `RequestRedirect` exception with the target for the redirect.
+
+ Keep in mind that the URL will be joined against the URL root of the
+ script so don't use a leading slash on the target URL unless you
+ really mean root of that domain.
+
+ `alias`
+ If enabled this rule serves as an alias for another rule with the same
+ endpoint and arguments.
+
+ `host`
+ If provided and the URL map has host matching enabled this can be
+ used to provide a match rule for the whole host. This also means
+ that the subdomain feature is disabled.
+
+ .. versionadded:: 0.7
+ The `alias` and `host` parameters were added.
+ """
+
+ def __init__(self, string, defaults=None, subdomain=None, methods=None,
+ build_only=False, endpoint=None, strict_slashes=None,
+ redirect_to=None, alias=False, host=None):
+ if not string.startswith('/'):
+ raise ValueError('urls must start with a leading slash')
+ self.rule = string
+ self.is_leaf = not string.endswith('/')
+
+ self.map = None
+ self.strict_slashes = strict_slashes
+ self.subdomain = subdomain
+ self.host = host
+ self.defaults = defaults
+ self.build_only = build_only
+ self.alias = alias
+ if methods is None:
+ self.methods = None
+ else:
+ self.methods = set([x.upper() for x in methods])
+ if 'HEAD' not in self.methods and 'GET' in self.methods:
+ self.methods.add('HEAD')
+ self.endpoint = endpoint
+ self.redirect_to = redirect_to
+
+ if defaults:
+ self.arguments = set(map(str, defaults))
+ else:
+ self.arguments = set()
+ self._trace = self._converters = self._regex = self._weights = None
+
+ def empty(self):
+ """Return an unbound copy of this rule. This can be useful if you
+ want to reuse an already bound URL for another map."""
+ defaults = None
+ if self.defaults:
+ defaults = dict(self.defaults)
+ return Rule(self.rule, defaults, self.subdomain, self.methods,
+ self.build_only, self.endpoint, self.strict_slashes,
+ self.redirect_to, self.alias, self.host)
+
+ def get_rules(self, map):
+ yield self
+
+ def refresh(self):
+ """Rebinds and refreshes the URL. Call this if you modified the
+ rule in place.
+
+ :internal:
+ """
+ self.bind(self.map, rebind=True)
+
+ def bind(self, map, rebind=False):
+ """Bind the url to a map and create a regular expression based on
+ the information from the rule itself and the defaults from the map.
+
+ :internal:
+ """
+ if self.map is not None and not rebind:
+ raise RuntimeError('url rule %r already bound to map %r' %
+ (self, self.map))
+ self.map = map
+ if self.strict_slashes is None:
+ self.strict_slashes = map.strict_slashes
+ if self.subdomain is None:
+ self.subdomain = map.default_subdomain
+ self.compile()
+
+ def compile(self):
+ """Compiles the regular expression and stores it."""
+ assert self.map is not None, 'rule not bound'
+
+ if self.map.host_matching:
+ domain_rule = self.host or ''
+ else:
+ domain_rule = self.subdomain or ''
+
+ self._trace = []
+ self._converters = {}
+ self._weights = []
+ regex_parts = []
+
+ def _build_regex(rule):
+ for converter, arguments, variable in parse_rule(rule):
+ if converter is None:
+ regex_parts.append(re.escape(variable))
+ self._trace.append((False, variable))
+ for part in variable.split('/'):
+ if part:
+ self._weights.append((0, -len(part)))
+ else:
+ convobj = get_converter(self.map, converter, arguments)
+ regex_parts.append('(?P<%s>%s)' % (variable, convobj.regex))
+ self._converters[variable] = convobj
+ self._trace.append((True, variable))
+ self._weights.append((1, convobj.weight))
+ self.arguments.add(str(variable))
+
+ _build_regex(domain_rule)
+ regex_parts.append('\\|')
+ self._trace.append((False, '|'))
+ _build_regex(self.is_leaf and self.rule or self.rule.rstrip('/'))
+ if not self.is_leaf:
+ self._trace.append((False, '/'))
+
+ if self.build_only:
+ return
+ regex = r'^%s%s$' % (
+ u''.join(regex_parts),
+ (not self.is_leaf or not self.strict_slashes) and \
+ '(?<!/)(?P<__suffix__>/?)' or ''
+ )
+ self._regex = re.compile(regex, re.UNICODE)
+
+ def match(self, path):
+ """Check if the rule matches a given path. Path is a string in the
+ form ``"subdomain|/path(method)"`` and is assembled by the map. If
+ the map is doing host matching the subdomain part will be the host
+ instead.
+
+ If the rule matches a dict with the converted values is returned,
+ otherwise the return value is `None`.
+
+ :internal:
+ """
+ if not self.build_only:
+ m = self._regex.search(path)
+ if m is not None:
+ groups = m.groupdict()
+ # we have a folder like part of the url without a trailing
+ # slash and strict slashes enabled. raise an exception that
+ # tells the map to redirect to the same url but with a
+ # trailing slash
+ if self.strict_slashes and not self.is_leaf and \
+ not groups.pop('__suffix__'):
+ raise RequestSlash()
+ # if we are not in strict slashes mode we have to remove
+ # a __suffix__
+ elif not self.strict_slashes:
+ del groups['__suffix__']
+
+ result = {}
+ for name, value in groups.iteritems():
+ try:
+ value = self._converters[name].to_python(value)
+ except ValidationError:
+ return
+ result[str(name)] = value
+ if self.defaults:
+ result.update(self.defaults)
+
+ if self.alias and self.map.redirect_defaults:
+ raise RequestAliasRedirect(result)
+
+ return result
+
+ def build(self, values, append_unknown=True):
+ """Assembles the relative url for that rule and the subdomain.
+ If building doesn't work for some reasons `None` is returned.
+
+ :internal:
+ """
+ tmp = []
+ add = tmp.append
+ processed = set(self.arguments)
+ for is_dynamic, data in self._trace:
+ if is_dynamic:
+ try:
+ add(self._converters[data].to_url(values[data]))
+ except ValidationError:
+ return
+ processed.add(data)
+ else:
+ add(data)
+ domain_part, url = (u''.join(tmp)).split('|', 1)
+
+ if append_unknown:
+ query_vars = MultiDict(values)
+ for key in processed:
+ if key in query_vars:
+ del query_vars[key]
+
+ if query_vars:
+ url += '?' + url_encode(query_vars, self.map.charset,
+ sort=self.map.sort_parameters,
+ key=self.map.sort_key)
+
+ return domain_part, url
+
+ def provides_defaults_for(self, rule):
+ """Check if this rule has defaults for a given rule.
+
+ :internal:
+ """
+ return not self.build_only and self.defaults and \
+ self.endpoint == rule.endpoint and self != rule and \
+ self.arguments == rule.arguments
+
+ def suitable_for(self, values, method=None):
+ """Check if the dict of values has enough data for url generation.
+
+ :internal:
+ """
+ # if a method was given explicitly and that method is not supported
+ # by this rule, this rule is not suitable.
+ if method is not None and self.methods is not None \
+ and method not in self.methods:
+ return False
+
+ defaults = self.defaults or ()
+
+ # all arguments required must be either in the defaults dict or
+ # the value dictionary otherwise it's not suitable
+ for key in self.arguments:
+ if key not in defaults and key not in values:
+ return False
+
+ # in case defaults are given we ensure taht either the value was
+ # skipped or the value is the same as the default value.
+ if defaults:
+ for key, value in defaults.iteritems():
+ if key in values and value != values[key]:
+ return False
+
+ return True
+
+ def match_compare_key(self):
+ """The match compare key for sorting.
+
+ Current implementation:
+
+ 1. rules without any arguments come first for performance
+ reasons only as we expect them to match faster and some
+ common ones usually don't have any arguments (index pages etc.)
+ 2. The more complex rules come first so the second argument is the
+ negative length of the number of weights.
+ 3. lastly we order by the actual weights.
+
+ :internal:
+ """
+ return bool(self.arguments), -len(self._weights), self._weights
+
+ def build_compare_key(self):
+ """The build compare key for sorting.
+
+ :internal:
+ """
+ return self.alias and 1 or 0, -len(self.arguments), \
+ -len(self.defaults or ())
+
+ def __eq__(self, other):
+ return self.__class__ is other.__class__ and \
+ self._trace == other._trace
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __unicode__(self):
+ return self.rule
+
+ def __str__(self):
+ charset = self.map is not None and self.map.charset or 'utf-8'
+ return unicode(self).encode(charset)
+
+ def __repr__(self):
+ if self.map is None:
+ return '<%s (unbound)>' % self.__class__.__name__
+ charset = self.map is not None and self.map.charset or 'utf-8'
+ tmp = []
+ for is_dynamic, data in self._trace:
+ if is_dynamic:
+ tmp.append('<%s>' % data)
+ else:
+ tmp.append(data)
+ return '<%s %r%s -> %s>' % (
+ self.__class__.__name__,
+ (u''.join(tmp).encode(charset)).lstrip('|'),
+ self.methods is not None and ' (%s)' % \
+ ', '.join(self.methods) or '',
+ self.endpoint
+ )
+
+
+class BaseConverter(object):
+ """Base class for all converters."""
+ regex = '[^/]+'
+ weight = 100
+
+ def __init__(self, map):
+ self.map = map
+
+ def to_python(self, value):
+ return value
+
+ def to_url(self, value):
+ return url_quote(value, self.map.charset)
+
+
+class UnicodeConverter(BaseConverter):
+ """This converter is the default converter and accepts any string but
+ only one path segment. Thus the string can not include a slash.
+
+ This is the default validator.
+
+ Example::
+
+ Rule('/pages/<page>'),
+ Rule('/<string(length=2):lang_code>')
+
+ :param map: the :class:`Map`.
+ :param minlength: the minimum length of the string. Must be greater
+ or equal 1.
+ :param maxlength: the maximum length of the string.
+ :param length: the exact length of the string.
+ """
+
+ def __init__(self, map, minlength=1, maxlength=None, length=None):
+ BaseConverter.__init__(self, map)
+ if length is not None:
+ length = '{%d}' % int(length)
+ else:
+ if maxlength is None:
+ maxlength = ''
+ else:
+ maxlength = int(maxlength)
+ length = '{%s,%s}' % (
+ int(minlength),
+ maxlength
+ )
+ self.regex = '[^/]' + length
+
+
+class AnyConverter(BaseConverter):
+ """Matches one of the items provided. Items can either be Python
+ identifiers or strings::
+
+ Rule('/<any(about, help, imprint, class, "foo,bar"):page_name>')
+
+ :param map: the :class:`Map`.
+ :param items: this function accepts the possible items as positional
+ arguments.
+ """
+
+ def __init__(self, map, *items):
+ BaseConverter.__init__(self, map)
+ self.regex = '(?:%s)' % '|'.join([re.escape(x) for x in items])
+
+
+class PathConverter(BaseConverter):
+ """Like the default :class:`UnicodeConverter`, but it also matches
+ slashes. This is useful for wikis and similar applications::
+
+ Rule('/<path:wikipage>')
+ Rule('/<path:wikipage>/edit')
+
+ :param map: the :class:`Map`.
+ """
+ regex = '[^/].*?'
+ weight = 200
+
+
+class NumberConverter(BaseConverter):
+ """Baseclass for `IntegerConverter` and `FloatConverter`.
+
+ :internal:
+ """
+ weight = 50
+
+ def __init__(self, map, fixed_digits=0, min=None, max=None):
+ BaseConverter.__init__(self, map)
+ self.fixed_digits = fixed_digits
+ self.min = min
+ self.max = max
+
+ def to_python(self, value):
+ if (self.fixed_digits and len(value) != self.fixed_digits):
+ raise ValidationError()
+ value = self.num_convert(value)
+ if (self.min is not None and value < self.min) or \
+ (self.max is not None and value > self.max):
+ raise ValidationError()
+ return value
+
+ def to_url(self, value):
+ value = self.num_convert(value)
+ if self.fixed_digits:
+ value = ('%%0%sd' % self.fixed_digits) % value
+ return str(value)
+
+
+class IntegerConverter(NumberConverter):
+ """This converter only accepts integer values::
+
+ Rule('/page/<int:page>')
+
+ This converter does not support negative values.
+
+ :param map: the :class:`Map`.
+ :param fixed_digits: the number of fixed digits in the URL. If you set
+ this to ``4`` for example, the application will
+ only match if the url looks like ``/0001/``. The
+ default is variable length.
+ :param min: the minimal value.
+ :param max: the maximal value.
+ """
+ regex = r'\d+'
+ num_convert = int
+
+
+class FloatConverter(NumberConverter):
+ """This converter only accepts floating point values::
+
+ Rule('/probability/<float:probability>')
+
+ This converter does not support negative values.
+
+ :param map: the :class:`Map`.
+ :param min: the minimal value.
+ :param max: the maximal value.
+ """
+ regex = r'\d+\.\d+'
+ num_convert = float
+
+ def __init__(self, map, min=None, max=None):
+ NumberConverter.__init__(self, map, 0, min, max)
+
+
+#: the default converter mapping for the map.
+DEFAULT_CONVERTERS = {
+ 'default': UnicodeConverter,
+ 'string': UnicodeConverter,
+ 'any': AnyConverter,
+ 'path': PathConverter,
+ 'int': IntegerConverter,
+ 'float': FloatConverter
+}
+
+
+class Map(object):
+ """The map class stores all the URL rules and some configuration
+ parameters. Some of the configuration values are only stored on the
+ `Map` instance since those affect all rules, others are just defaults
+ and can be overridden for each rule. Note that you have to specify all
+ arguments besides the `rules` as keyword arguments!
+
+ :param rules: sequence of url rules for this map.
+ :param default_subdomain: The default subdomain for rules without a
+ subdomain defined.
+ :param charset: charset of the url. defaults to ``"utf-8"``
+ :param strict_slashes: Take care of trailing slashes.
+ :param redirect_defaults: This will redirect to the default rule if it
+ wasn't visited that way. This helps creating
+ unique URLs.
+ :param converters: A dict of converters that adds additional converters
+ to the list of converters. If you redefine one
+ converter this will override the original one.
+ :param sort_parameters: If set to `True` the url parameters are sorted.
+ See `url_encode` for more details.
+ :param sort_key: The sort key function for `url_encode`.
+ :param encoding_errors: the error method to use for decoding
+ :param host_matching: if set to `True` it enables the host matching
+ feature and disables the subdomain one. If
+ enabled the `host` parameter to rules is used
+ instead of the `subdomain` one.
+
+ .. versionadded:: 0.5
+ `sort_parameters` and `sort_key` was added.
+
+ .. versionadded:: 0.7
+ `encoding_errors` and `host_matching` was added.
+ """
+
+ #: .. versionadded:: 0.6
+ #: a dict of default converters to be used.
+ default_converters = ImmutableDict(DEFAULT_CONVERTERS)
+
+ def __init__(self, rules=None, default_subdomain='', charset='utf-8',
+ strict_slashes=True, redirect_defaults=True,
+ converters=None, sort_parameters=False, sort_key=None,
+ encoding_errors='replace', host_matching=False):
+ self._rules = []
+ self._rules_by_endpoint = {}
+ self._remap = True
+
+ self.default_subdomain = default_subdomain
+ self.charset = charset
+ self.encoding_errors = encoding_errors
+ self.strict_slashes = strict_slashes
+ self.redirect_defaults = redirect_defaults
+ self.host_matching = host_matching
+
+ self.converters = self.default_converters.copy()
+ if converters:
+ self.converters.update(converters)
+
+ self.sort_parameters = sort_parameters
+ self.sort_key = sort_key
+
+ for rulefactory in rules or ():
+ self.add(rulefactory)
+
+ def is_endpoint_expecting(self, endpoint, *arguments):
+ """Iterate over all rules and check if the endpoint expects
+ the arguments provided. This is for example useful if you have
+ some URLs that expect a language code and others that do not and
+ you want to wrap the builder a bit so that the current language
+ code is automatically added if not provided but endpoints expect
+ it.
+
+ :param endpoint: the endpoint to check.
+ :param arguments: this function accepts one or more arguments
+ as positional arguments. Each one of them is
+ checked.
+ """
+ self.update()
+ arguments = set(arguments)
+ for rule in self._rules_by_endpoint[endpoint]:
+ if arguments.issubset(rule.arguments):
+ return True
+ return False
+
+ def iter_rules(self, endpoint=None):
+ """Iterate over all rules or the rules of an endpoint.
+
+ :param endpoint: if provided only the rules for that endpoint
+ are returned.
+ :return: an iterator
+ """
+ self.update()
+ if endpoint is not None:
+ return iter(self._rules_by_endpoint[endpoint])
+ return iter(self._rules)
+
+ def add(self, rulefactory):
+ """Add a new rule or factory to the map and bind it. Requires that the
+ rule is not bound to another map.
+
+ :param rulefactory: a :class:`Rule` or :class:`RuleFactory`
+ """
+ for rule in rulefactory.get_rules(self):
+ rule.bind(self)
+ self._rules.append(rule)
+ self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule)
+ self._remap = True
+
+ def bind(self, server_name, script_name=None, subdomain=None,
+ url_scheme='http', default_method='GET', path_info=None,
+ query_args=None):
+ """Return a new :class:`MapAdapter` with the details specified to the
+ call. Note that `script_name` will default to ``'/'`` if not further
+ specified or `None`. The `server_name` at least is a requirement
+ because the HTTP RFC requires absolute URLs for redirects and so all
+ redirect exceptions raised by Werkzeug will contain the full canonical
+ URL.
+
+ If no path_info is passed to :meth:`match` it will use the default path
+ info passed to bind. While this doesn't really make sense for
+ manual bind calls, it's useful if you bind a map to a WSGI
+ environment which already contains the path info.
+
+ `subdomain` will default to the `default_subdomain` for this map if
+ no defined. If there is no `default_subdomain` you cannot use the
+ subdomain feature.
+
+ .. versionadded:: 0.7
+ `query_args` added
+
+ .. versionadded:: 0.8
+ `query_args` can now also be a string.
+ """
+ server_name = server_name.lower()
+ if self.host_matching:
+ if subdomain is not None:
+ raise RuntimeError('host matching enabled and a '
+ 'subdomain was provided')
+ elif subdomain is None:
+ subdomain = self.default_subdomain
+ if script_name is None:
+ script_name = '/'
+ return MapAdapter(self, server_name, script_name, subdomain,
+ url_scheme, path_info, default_method, query_args)
+
+ def bind_to_environ(self, environ, server_name=None, subdomain=None):
+ """Like :meth:`bind` but you can pass it an WSGI environment and it
+ will fetch the information from that dictionary. Note that because of
+ limitations in the protocol there is no way to get the current
+ subdomain and real `server_name` from the environment. If you don't
+ provide it, Werkzeug will use `SERVER_NAME` and `SERVER_PORT` (or
+ `HTTP_HOST` if provided) as used `server_name` with disabled subdomain
+ feature.
+
+ If `subdomain` is `None` but an environment and a server name is
+ provided it will calculate the current subdomain automatically.
+ Example: `server_name` is ``'example.com'`` and the `SERVER_NAME`
+ in the wsgi `environ` is ``'staging.dev.example.com'`` the calculated
+ subdomain will be ``'staging.dev'``.
+
+ If the object passed as environ has an environ attribute, the value of
+ this attribute is used instead. This allows you to pass request
+ objects. Additionally `PATH_INFO` added as a default of the
+ :class:`MapAdapter` so that you don't have to pass the path info to
+ the match method.
+
+ .. versionchanged:: 0.5
+ previously this method accepted a bogus `calculate_subdomain`
+ parameter that did not have any effect. It was removed because
+ of that.
+
+ .. versionchanged:: 0.8
+ This will no longer raise a ValueError when an unexpected server
+ name was passed.
+
+ :param environ: a WSGI environment.
+ :param server_name: an optional server name hint (see above).
+ :param subdomain: optionally the current subdomain (see above).
+ """
+ environ = _get_environ(environ)
+ if server_name is None:
+ if 'HTTP_HOST' in environ:
+ server_name = environ['HTTP_HOST']
+ else:
+ server_name = environ['SERVER_NAME']
+ if (environ['wsgi.url_scheme'], environ['SERVER_PORT']) not \
+ in (('https', '443'), ('http', '80')):
+ server_name += ':' + environ['SERVER_PORT']
+ elif subdomain is None and not self.host_matching:
+ server_name = server_name.lower()
+ if 'HTTP_HOST' in environ:
+ wsgi_server_name = environ.get('HTTP_HOST')
+ else:
+ wsgi_server_name = environ.get('SERVER_NAME')
+ if (environ['wsgi.url_scheme'], environ['SERVER_PORT']) not \
+ in (('https', '443'), ('http', '80')):
+ wsgi_server_name += ':' + environ['SERVER_PORT']
+ wsgi_server_name = wsgi_server_name.lower()
+ cur_server_name = wsgi_server_name.split('.')
+ real_server_name = server_name.split('.')
+ offset = -len(real_server_name)
+ if cur_server_name[offset:] != real_server_name:
+ # This can happen even with valid configs if the server was
+ # accesssed directly by IP address under some situations.
+ # Instead of raising an exception like in Werkzeug 0.7 or
+ # earlier we go by an invalid subdomain which will result
+ # in a 404 error on matching.
+ subdomain = '<invalid>'
+ else:
+ subdomain = '.'.join(filter(None, cur_server_name[:offset]))
+ return Map.bind(self, server_name, environ.get('SCRIPT_NAME'),
+ subdomain, environ['wsgi.url_scheme'],
+ environ['REQUEST_METHOD'], environ.get('PATH_INFO'),
+ query_args=environ.get('QUERY_STRING', ''))
+
+ def update(self):
+ """Called before matching and building to keep the compiled rules
+ in the correct order after things changed.
+ """
+ if self._remap:
+ self._rules.sort(key=lambda x: x.match_compare_key())
+ for rules in self._rules_by_endpoint.itervalues():
+ rules.sort(key=lambda x: x.build_compare_key())
+ self._remap = False
+
+ def __repr__(self):
+ rules = self.iter_rules()
+ return '%s(%s)' % (self.__class__.__name__, pformat(list(rules)))
+
+
+class MapAdapter(object):
+ """Returned by :meth:`Map.bind` or :meth:`Map.bind_to_environ` and does
+ the URL matching and building based on runtime information.
+ """
+
+ def __init__(self, map, server_name, script_name, subdomain,
+ url_scheme, path_info, default_method, query_args=None):
+ self.map = map
+ self.server_name = server_name
+ if not script_name.endswith('/'):
+ script_name += '/'
+ self.script_name = script_name
+ self.subdomain = subdomain
+ self.url_scheme = url_scheme
+ self.path_info = path_info or u''
+ self.default_method = default_method
+ self.query_args = query_args
+
+ def dispatch(self, view_func, path_info=None, method=None,
+ catch_http_exceptions=False):
+ """Does the complete dispatching process. `view_func` is called with
+ the endpoint and a dict with the values for the view. It should
+ look up the view function, call it, and return a response object
+ or WSGI application. http exceptions are not caught by default
+ so that applications can display nicer error messages by just
+ catching them by hand. If you want to stick with the default
+ error messages you can pass it ``catch_http_exceptions=True`` and
+ it will catch the http exceptions.
+
+ Here a small example for the dispatch usage::
+
+ from werkzeug.wrappers import Request, Response
+ from werkzeug.wsgi import responder
+ from werkzeug.routing import Map, Rule
+
+ def on_index(request):
+ return Response('Hello from the index')
+
+ url_map = Map([Rule('/', endpoint='index')])
+ views = {'index': on_index}
+
+ @responder
+ def application(environ, start_response):
+ request = Request(environ)
+ urls = url_map.bind_to_environ(environ)
+ return urls.dispatch(lambda e, v: views[e](request, **v),
+ catch_http_exceptions=True)
+
+ Keep in mind that this method might return exception objects, too, so
+ use :class:`Response.force_type` to get a response object.
+
+ :param view_func: a function that is called with the endpoint as
+ first argument and the value dict as second. Has
+ to dispatch to the actual view function with this
+ information. (see above)
+ :param path_info: the path info to use for matching. Overrides the
+ path info specified on binding.
+ :param method: the HTTP method used for matching. Overrides the
+ method specified on binding.
+ :param catch_http_exceptions: set to `True` to catch any of the
+ werkzeug :class:`HTTPException`\s.
+ """
+ try:
+ try:
+ endpoint, args = self.match(path_info, method)
+ except RequestRedirect, e:
+ return e
+ return view_func(endpoint, args)
+ except HTTPException, e:
+ if catch_http_exceptions:
+ return e
+ raise
+
+ def match(self, path_info=None, method=None, return_rule=False,
+ query_args=None):
+ """The usage is simple: you just pass the match method the current
+ path info as well as the method (which defaults to `GET`). The
+ following things can then happen:
+
+ - you receive a `NotFound` exception that indicates that no URL is
+ matching. A `NotFound` exception is also a WSGI application you
+ can call to get a default page not found page (happens to be the
+ same object as `werkzeug.exceptions.NotFound`)
+
+ - you receive a `MethodNotAllowed` exception that indicates that there
+ is a match for this URL but not for the current request method.
+ This is useful for RESTful applications.
+
+ - you receive a `RequestRedirect` exception with a `new_url`
+ attribute. This exception is used to notify you about a request
+ Werkzeug requests from your WSGI application. This is for example the
+ case if you request ``/foo`` although the correct URL is ``/foo/``
+ You can use the `RequestRedirect` instance as response-like object
+ similar to all other subclasses of `HTTPException`.
+
+ - you get a tuple in the form ``(endpoint, arguments)`` if there is
+ a match (unless `return_rule` is True, in which case you get a tuple
+ in the form ``(rule, arguments)``)
+
+ If the path info is not passed to the match method the default path
+ info of the map is used (defaults to the root URL if not defined
+ explicitly).
+
+ All of the exceptions raised are subclasses of `HTTPException` so they
+ can be used as WSGI responses. The will all render generic error or
+ redirect pages.
+
+ Here is a small example for matching:
+
+ >>> m = Map([
+ ... Rule('/', endpoint='index'),
+ ... Rule('/downloads/', endpoint='downloads/index'),
+ ... Rule('/downloads/<int:id>', endpoint='downloads/show')
+ ... ])
+ >>> urls = m.bind("example.com", "/")
+ >>> urls.match("/", "GET")
+ ('index', {})
+ >>> urls.match("/downloads/42")
+ ('downloads/show', {'id': 42})
+
+ And here is what happens on redirect and missing URLs:
+
+ >>> urls.match("/downloads")
+ Traceback (most recent call last):
+ ...
+ RequestRedirect: http://example.com/downloads/
+ >>> urls.match("/missing")
+ Traceback (most recent call last):
+ ...
+ NotFound: 404 Not Found
+
+ :param path_info: the path info to use for matching. Overrides the
+ path info specified on binding.
+ :param method: the HTTP method used for matching. Overrides the
+ method specified on binding.
+ :param return_rule: return the rule that matched instead of just the
+ endpoint (defaults to `False`).
+ :param query_args: optional query arguments that are used for
+ automatic redirects as string or dictionary. It's
+ currently not possible to use the query arguments
+ for URL matching.
+
+ .. versionadded:: 0.6
+ `return_rule` was added.
+
+ .. versionadded:: 0.7
+ `query_args` was added.
+
+ .. versionchanged:: 0.8
+ `query_args` can now also be a string.
+ """
+ self.map.update()
+ if path_info is None:
+ path_info = self.path_info
+ if not isinstance(path_info, unicode):
+ path_info = path_info.decode(self.map.charset,
+ self.map.encoding_errors)
+ if query_args is None:
+ query_args = self.query_args
+ method = (method or self.default_method).upper()
+
+ path = u'%s|/%s' % (self.map.host_matching and self.server_name or
+ self.subdomain, path_info.lstrip('/'))
+
+ have_match_for = set()
+ for rule in self.map._rules:
+ try:
+ rv = rule.match(path)
+ except RequestSlash:
+ raise RequestRedirect(self.make_redirect_url(
+ path_info + '/', query_args))
+ except RequestAliasRedirect, e:
+ raise RequestRedirect(self.make_alias_redirect_url(
+ path, rule.endpoint, e.matched_values, method, query_args))
+ if rv is None:
+ continue
+ if rule.methods is not None and method not in rule.methods:
+ have_match_for.update(rule.methods)
+ continue
+
+ if self.map.redirect_defaults:
+ redirect_url = self.get_default_redirect(rule, method, rv,
+ query_args)
+ if redirect_url is not None:
+ raise RequestRedirect(redirect_url)
+
+ if rule.redirect_to is not None:
+ if isinstance(rule.redirect_to, basestring):
+ def _handle_match(match):
+ value = rv[match.group(1)]
+ return rule._converters[match.group(1)].to_url(value)
+ redirect_url = _simple_rule_re.sub(_handle_match,
+ rule.redirect_to)
+ else:
+ redirect_url = rule.redirect_to(self, **rv)
+ raise RequestRedirect(str(urljoin('%s://%s%s%s' % (
+ self.url_scheme,
+ self.subdomain and self.subdomain + '.' or '',
+ self.server_name,
+ self.script_name
+ ), redirect_url)))
+
+ if return_rule:
+ return rule, rv
+ else:
+ return rule.endpoint, rv
+
+ if have_match_for:
+ raise MethodNotAllowed(valid_methods=list(have_match_for))
+ raise NotFound()
+
+ def test(self, path_info=None, method=None):
+ """Test if a rule would match. Works like `match` but returns `True`
+ if the URL matches, or `False` if it does not exist.
+
+ :param path_info: the path info to use for matching. Overrides the
+ path info specified on binding.
+ :param method: the HTTP method used for matching. Overrides the
+ method specified on binding.
+ """
+ try:
+ self.match(path_info, method)
+ except RequestRedirect:
+ pass
+ except HTTPException:
+ return False
+ return True
+
+ def allowed_methods(self, path_info=None):
+ """Returns the valid methods that match for a given path.
+
+ .. versionadded:: 0.7
+ """
+ try:
+ self.match(path_info, method='--')
+ except MethodNotAllowed, e:
+ return e.valid_methods
+ except HTTPException, e:
+ pass
+ return []
+
+ def get_host(self, domain_part):
+ """Figures out the full host name for the given domain part. The
+ domain part is a subdomain in case host matching is disabled or
+ a full host name.
+ """
+ if self.map.host_matching:
+ if domain_part is None:
+ return self.server_name
+ return domain_part
+ subdomain = domain_part
+ if subdomain is None:
+ subdomain = self.subdomain
+ return (subdomain and subdomain + '.' or '') + self.server_name
+
+ def get_default_redirect(self, rule, method, values, query_args):
+ """A helper that returns the URL to redirect to if it finds one.
+ This is used for default redirecting only.
+
+ :internal:
+ """
+ assert self.map.redirect_defaults
+ for r in self.map._rules_by_endpoint[rule.endpoint]:
+ # every rule that comes after this one, including ourself
+ # has a lower priority for the defaults. We order the ones
+ # with the highest priority up for building.
+ if r is rule:
+ break
+ if r.provides_defaults_for(rule) and \
+ r.suitable_for(values, method):
+ values.update(r.defaults)
+ domain_part, path = r.build(values)
+ return self.make_redirect_url(
+ path, query_args, domain_part=domain_part)
+
+ def encode_query_args(self, query_args):
+ if not isinstance(query_args, basestring):
+ query_args = url_encode(query_args, self.map.charset)
+ return query_args
+
+ def make_redirect_url(self, path_info, query_args=None, domain_part=None):
+ """Creates a redirect URL.
+
+ :internal:
+ """
+ suffix = ''
+ if query_args:
+ suffix = '?' + self.encode_query_args(query_args)
+ return str('%s://%s/%s%s' % (
+ self.url_scheme,
+ self.get_host(domain_part),
+ posixpath.join(self.script_name[:-1].lstrip('/'),
+ url_quote(path_info.lstrip('/'), self.map.charset)),
+ suffix
+ ))
+
+ def make_alias_redirect_url(self, path, endpoint, values, method, query_args):
+ """Internally called to make an alias redirect URL."""
+ url = self.build(endpoint, values, method, append_unknown=False,
+ force_external=True)
+ if query_args:
+ url += '?' + self.encode_query_args(query_args)
+ assert url != path, 'detected invalid alias setting. No canonical ' \
+ 'URL found'
+ return url
+
+ def _partial_build(self, endpoint, values, method, append_unknown):
+ """Helper for :meth:`build`. Returns subdomain and path for the
+ rule that accepts this endpoint, values and method.
+
+ :internal:
+ """
+ # in case the method is none, try with the default method first
+ if method is None:
+ rv = self._partial_build(endpoint, values, self.default_method,
+ append_unknown)
+ if rv is not None:
+ return rv
+
+ # default method did not match or a specific method is passed,
+ # check all and go with first result.
+ for rule in self.map._rules_by_endpoint.get(endpoint, ()):
+ if rule.suitable_for(values, method):
+ rv = rule.build(values, append_unknown)
+ if rv is not None:
+ return rv
+
+ def build(self, endpoint, values=None, method=None, force_external=False,
+ append_unknown=True):
+ """Building URLs works pretty much the other way round. Instead of
+ `match` you call `build` and pass it the endpoint and a dict of
+ arguments for the placeholders.
+
+ The `build` function also accepts an argument called `force_external`
+ which, if you set it to `True` will force external URLs. Per default
+ external URLs (include the server name) will only be used if the
+ target URL is on a different subdomain.
+
+ >>> m = Map([
+ ... Rule('/', endpoint='index'),
+ ... Rule('/downloads/', endpoint='downloads/index'),
+ ... Rule('/downloads/<int:id>', endpoint='downloads/show')
+ ... ])
+ >>> urls = m.bind("example.com", "/")
+ >>> urls.build("index", {})
+ '/'
+ >>> urls.build("downloads/show", {'id': 42})
+ '/downloads/42'
+ >>> urls.build("downloads/show", {'id': 42}, force_external=True)
+ 'http://example.com/downloads/42'
+
+ Because URLs cannot contain non ASCII data you will always get
+ bytestrings back. Non ASCII characters are urlencoded with the
+ charset defined on the map instance.
+
+ Additional values are converted to unicode and appended to the URL as
+ URL querystring parameters:
+
+ >>> urls.build("index", {'q': 'My Searchstring'})
+ '/?q=My+Searchstring'
+
+ If a rule does not exist when building a `BuildError` exception is
+ raised.
+
+ The build method accepts an argument called `method` which allows you
+ to specify the method you want to have an URL built for if you have
+ different methods for the same endpoint specified.
+
+ .. versionadded:: 0.6
+ the `append_unknown` parameter was added.
+
+ :param endpoint: the endpoint of the URL to build.
+ :param values: the values for the URL to build. Unhandled values are
+ appended to the URL as query parameters.
+ :param method: the HTTP method for the rule if there are different
+ URLs for different methods on the same endpoint.
+ :param force_external: enforce full canonical external URLs.
+ :param append_unknown: unknown parameters are appended to the generated
+ URL as query string argument. Disable this
+ if you want the builder to ignore those.
+ """
+ self.map.update()
+ if values:
+ if isinstance(values, MultiDict):
+ valueiter = values.iteritems(multi=True)
+ else:
+ valueiter = values.iteritems()
+ values = dict((k, v) for k, v in valueiter if v is not None)
+ else:
+ values = {}
+
+ rv = self._partial_build(endpoint, values, method, append_unknown)
+ if rv is None:
+ raise BuildError(endpoint, values, method)
+ domain_part, path = rv
+
+ host = self.get_host(domain_part)
+
+ # shortcut this.
+ if not force_external and (
+ (self.map.host_matching and host == self.server_name) or
+ (not self.map.host_matching and domain_part == self.subdomain)):
+ return str(urljoin(self.script_name, './' + path.lstrip('/')))
+ return str('%s://%s%s/%s' % (
+ self.url_scheme,
+ host,
+ self.script_name[:-1],
+ path.lstrip('/')
+ ))
diff --git a/websdk/werkzeug/script.py b/websdk/werkzeug/script.py
new file mode 100644
index 0000000..2411cbd
--- /dev/null
+++ b/websdk/werkzeug/script.py
@@ -0,0 +1,313 @@
+# -*- coding: utf-8 -*-
+r'''
+ werkzeug.script
+ ~~~~~~~~~~~~~~~
+
+ .. admonition:: Deprecated Functionality
+
+ ``werkzeug.script`` is deprecated without replacement functionality.
+ Python's command line support improved greatly with :mod:`argparse`
+ and a bunch of alternative modules.
+
+ Most of the time you have recurring tasks while writing an application
+ such as starting up an interactive python interpreter with some prefilled
+ imports, starting the development server, initializing the database or
+ something similar.
+
+ For that purpose werkzeug provides the `werkzeug.script` module which
+ helps you writing such scripts.
+
+
+ Basic Usage
+ -----------
+
+ The following snippet is roughly the same in every werkzeug script::
+
+ #!/usr/bin/env python
+ # -*- coding: utf-8 -*-
+ from werkzeug import script
+
+ # actions go here
+
+ if __name__ == '__main__':
+ script.run()
+
+ Starting this script now does nothing because no actions are defined.
+ An action is a function in the same module starting with ``"action_"``
+ which takes a number of arguments where every argument has a default. The
+ type of the default value specifies the type of the argument.
+
+ Arguments can then be passed by position or using ``--name=value`` from
+ the shell.
+
+ Because a runserver and shell command is pretty common there are two
+ factory functions that create such commands::
+
+ def make_app():
+ from yourapplication import YourApplication
+ return YourApplication(...)
+
+ action_runserver = script.make_runserver(make_app, use_reloader=True)
+ action_shell = script.make_shell(lambda: {'app': make_app()})
+
+
+ Using The Scripts
+ -----------------
+
+ The script from above can be used like this from the shell now:
+
+ .. sourcecode:: text
+
+ $ ./manage.py --help
+ $ ./manage.py runserver localhost 8080 --debugger --no-reloader
+ $ ./manage.py runserver -p 4000
+ $ ./manage.py shell
+
+ As you can see it's possible to pass parameters as positional arguments
+ or as named parameters, pretty much like Python function calls.
+
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+'''
+import sys
+import inspect
+import getopt
+from os.path import basename
+
+
+argument_types = {
+ bool: 'boolean',
+ str: 'string',
+ int: 'integer',
+ float: 'float'
+}
+
+
+converters = {
+ 'boolean': lambda x: x.lower() in ('1', 'true', 'yes', 'on'),
+ 'string': str,
+ 'integer': int,
+ 'float': float
+}
+
+
+def run(namespace=None, action_prefix='action_', args=None):
+ """Run the script. Participating actions are looked up in the caller's
+ namespace if no namespace is given, otherwise in the dict provided.
+ Only items that start with action_prefix are processed as actions. If
+ you want to use all items in the namespace provided as actions set
+ action_prefix to an empty string.
+
+ :param namespace: An optional dict where the functions are looked up in.
+ By default the local namespace of the caller is used.
+ :param action_prefix: The prefix for the functions. Everything else
+ is ignored.
+ :param args: the arguments for the function. If not specified
+ :data:`sys.argv` without the first argument is used.
+ """
+ if namespace is None:
+ namespace = sys._getframe(1).f_locals
+ actions = find_actions(namespace, action_prefix)
+
+ if args is None:
+ args = sys.argv[1:]
+ if not args or args[0] in ('-h', '--help'):
+ return print_usage(actions)
+ elif args[0] not in actions:
+ fail('Unknown action \'%s\'' % args[0])
+
+ arguments = {}
+ types = {}
+ key_to_arg = {}
+ long_options = []
+ formatstring = ''
+ func, doc, arg_def = actions[args.pop(0)]
+ for idx, (arg, shortcut, default, option_type) in enumerate(arg_def):
+ real_arg = arg.replace('-', '_')
+ if shortcut:
+ formatstring += shortcut
+ if not isinstance(default, bool):
+ formatstring += ':'
+ key_to_arg['-' + shortcut] = real_arg
+ long_options.append(isinstance(default, bool) and arg or arg + '=')
+ key_to_arg['--' + arg] = real_arg
+ key_to_arg[idx] = real_arg
+ types[real_arg] = option_type
+ arguments[real_arg] = default
+
+ try:
+ optlist, posargs = getopt.gnu_getopt(args, formatstring, long_options)
+ except getopt.GetoptError, e:
+ fail(str(e))
+
+ specified_arguments = set()
+ for key, value in enumerate(posargs):
+ try:
+ arg = key_to_arg[key]
+ except IndexError:
+ fail('Too many parameters')
+ specified_arguments.add(arg)
+ try:
+ arguments[arg] = converters[types[arg]](value)
+ except ValueError:
+ fail('Invalid value for argument %s (%s): %s' % (key, arg, value))
+
+ for key, value in optlist:
+ arg = key_to_arg[key]
+ if arg in specified_arguments:
+ fail('Argument \'%s\' is specified twice' % arg)
+ if types[arg] == 'boolean':
+ if arg.startswith('no_'):
+ value = 'no'
+ else:
+ value = 'yes'
+ try:
+ arguments[arg] = converters[types[arg]](value)
+ except ValueError:
+ fail('Invalid value for \'%s\': %s' % (key, value))
+
+ newargs = {}
+ for k, v in arguments.iteritems():
+ newargs[k.startswith('no_') and k[3:] or k] = v
+ arguments = newargs
+ return func(**arguments)
+
+
+def fail(message, code=-1):
+ """Fail with an error."""
+ print >> sys.stderr, 'Error:', message
+ sys.exit(code)
+
+
+def find_actions(namespace, action_prefix):
+ """Find all the actions in the namespace."""
+ actions = {}
+ for key, value in namespace.iteritems():
+ if key.startswith(action_prefix):
+ actions[key[len(action_prefix):]] = analyse_action(value)
+ return actions
+
+
+def print_usage(actions):
+ """Print the usage information. (Help screen)"""
+ actions = actions.items()
+ actions.sort()
+ print 'usage: %s <action> [<options>]' % basename(sys.argv[0])
+ print ' %s --help' % basename(sys.argv[0])
+ print
+ print 'actions:'
+ for name, (func, doc, arguments) in actions:
+ print ' %s:' % name
+ for line in doc.splitlines():
+ print ' %s' % line
+ if arguments:
+ print
+ for arg, shortcut, default, argtype in arguments:
+ if isinstance(default, bool):
+ print ' %s' % (
+ (shortcut and '-%s, ' % shortcut or '') + '--' + arg
+ )
+ else:
+ print ' %-30s%-10s%s' % (
+ (shortcut and '-%s, ' % shortcut or '') + '--' + arg,
+ argtype, default
+ )
+ print
+
+
+def analyse_action(func):
+ """Analyse a function."""
+ description = inspect.getdoc(func) or 'undocumented action'
+ arguments = []
+ args, varargs, kwargs, defaults = inspect.getargspec(func)
+ if varargs or kwargs:
+ raise TypeError('variable length arguments for action not allowed.')
+ if len(args) != len(defaults or ()):
+ raise TypeError('not all arguments have proper definitions')
+
+ for idx, (arg, definition) in enumerate(zip(args, defaults or ())):
+ if arg.startswith('_'):
+ raise TypeError('arguments may not start with an underscore')
+ if not isinstance(definition, tuple):
+ shortcut = None
+ default = definition
+ else:
+ shortcut, default = definition
+ argument_type = argument_types[type(default)]
+ if isinstance(default, bool) and default is True:
+ arg = 'no-' + arg
+ arguments.append((arg.replace('_', '-'), shortcut,
+ default, argument_type))
+ return func, description, arguments
+
+
+def make_shell(init_func=None, banner=None, use_ipython=True):
+ """Returns an action callback that spawns a new interactive
+ python shell.
+
+ :param init_func: an optional initialization function that is
+ called before the shell is started. The return
+ value of this function is the initial namespace.
+ :param banner: the banner that is displayed before the shell. If
+ not specified a generic banner is used instead.
+ :param use_ipython: if set to `True` ipython is used if available.
+ """
+ if banner is None:
+ banner = 'Interactive Werkzeug Shell'
+ if init_func is None:
+ init_func = dict
+ def action(ipython=use_ipython):
+ """Start a new interactive python session."""
+ namespace = init_func()
+ if ipython:
+ try:
+ try:
+ from IPython.frontend.terminal.embed import InteractiveShellEmbed
+ sh = InteractiveShellEmbed(banner1=banner)
+ except ImportError:
+ from IPython.Shell import IPShellEmbed
+ sh = IPShellEmbed(banner=banner)
+ except ImportError:
+ pass
+ else:
+ sh(global_ns={}, local_ns=namespace)
+ return
+ from code import interact
+ interact(banner, local=namespace)
+ return action
+
+
+def make_runserver(app_factory, hostname='localhost', port=5000,
+ use_reloader=False, use_debugger=False, use_evalex=True,
+ threaded=False, processes=1, static_files=None,
+ extra_files=None, ssl_context=None):
+ """Returns an action callback that spawns a new development server.
+
+ .. versionadded:: 0.5
+ `static_files` and `extra_files` was added.
+
+ ..versionadded:: 0.6.1
+ `ssl_context` was added.
+
+ :param app_factory: a function that returns a new WSGI application.
+ :param hostname: the default hostname the server should listen on.
+ :param port: the default port of the server.
+ :param use_reloader: the default setting for the reloader.
+ :param use_evalex: the default setting for the evalex flag of the debugger.
+ :param threaded: the default threading setting.
+ :param processes: the default number of processes to start.
+ :param static_files: optional dict of static files.
+ :param extra_files: optional list of extra files to track for reloading.
+ :param ssl_context: optional SSL context for running server in HTTPS mode.
+ """
+ def action(hostname=('h', hostname), port=('p', port),
+ reloader=use_reloader, debugger=use_debugger,
+ evalex=use_evalex, threaded=threaded, processes=processes):
+ """Start a new development server."""
+ from werkzeug.serving import run_simple
+ app = app_factory()
+ run_simple(hostname, port, app, reloader, debugger, evalex,
+ extra_files, 1, threaded, processes,
+ static_files=static_files, ssl_context=ssl_context)
+ return action
diff --git a/websdk/werkzeug/security.py b/websdk/werkzeug/security.py
new file mode 100644
index 0000000..5f1d7d4
--- /dev/null
+++ b/websdk/werkzeug/security.py
@@ -0,0 +1,140 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.security
+ ~~~~~~~~~~~~~~~~~
+
+ Security related helpers such as secure password hashing tools.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import hmac
+import posixpath
+from itertools import izip
+from random import SystemRandom
+
+# because the API of hmac changed with the introduction of the
+# new hashlib module, we have to support both. This sets up a
+# mapping to the digest factory functions and the digest modules
+# (or factory functions with changed API)
+try:
+ from hashlib import sha1, md5
+ _hash_funcs = _hash_mods = {'sha1': sha1, 'md5': md5}
+ _sha1_mod = sha1
+ _md5_mod = md5
+except ImportError:
+ import sha as _sha1_mod, md5 as _md5_mod
+ _hash_mods = {'sha1': _sha1_mod, 'md5': _md5_mod}
+ _hash_funcs = {'sha1': _sha1_mod.new, 'md5': _md5_mod.new}
+
+
+SALT_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
+
+
+_sys_rng = SystemRandom()
+_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
+ if sep not in (None, '/'))
+
+
+def safe_str_cmp(a, b):
+ """This function compares strings in somewhat constant time. This
+ requires that the length of at least one string is known in advance.
+
+ Returns `True` if the two strings are equal or `False` if they are not.
+
+ .. versionadded:: 0.7
+ """
+ if len(a) != len(b):
+ return False
+ rv = 0
+ for x, y in izip(a, b):
+ rv |= ord(x) ^ ord(y)
+ return rv == 0
+
+
+def gen_salt(length):
+ """Generate a random string of SALT_CHARS with specified ``length``."""
+ if length <= 0:
+ raise ValueError('requested salt of length <= 0')
+ return ''.join(_sys_rng.choice(SALT_CHARS) for _ in xrange(length))
+
+
+def _hash_internal(method, salt, password):
+ """Internal password hash helper. Supports plaintext without salt,
+ unsalted and salted passwords. In case salted passwords are used
+ hmac is used.
+ """
+ if method == 'plain':
+ return password
+ if salt:
+ if method not in _hash_mods:
+ return None
+ if isinstance(salt, unicode):
+ salt = salt.encode('utf-8')
+ h = hmac.new(salt, None, _hash_mods[method])
+ else:
+ if method not in _hash_funcs:
+ return None
+ h = _hash_funcs[method]()
+ if isinstance(password, unicode):
+ password = password.encode('utf-8')
+ h.update(password)
+ return h.hexdigest()
+
+
+def generate_password_hash(password, method='sha1', salt_length=8):
+ """Hash a password with the given method and salt with with a string of
+ the given length. The format of the string returned includes the method
+ that was used so that :func:`check_password_hash` can check the hash.
+
+ The format for the hashed string looks like this::
+
+ method$salt$hash
+
+ This method can **not** generate unsalted passwords but it is possible
+ to set the method to plain to enforce plaintext passwords. If a salt
+ is used, hmac is used internally to salt the password.
+
+ :param password: the password to hash
+ :param method: the hash method to use (``'md5'`` or ``'sha1'``)
+ :param salt_length: the lengt of the salt in letters
+ """
+ salt = method != 'plain' and gen_salt(salt_length) or ''
+ h = _hash_internal(method, salt, password)
+ if h is None:
+ raise TypeError('invalid method %r' % method)
+ return '%s$%s$%s' % (method, salt, h)
+
+
+def check_password_hash(pwhash, password):
+ """check a password against a given salted and hashed password value.
+ In order to support unsalted legacy passwords this method supports
+ plain text passwords, md5 and sha1 hashes (both salted and unsalted).
+
+ Returns `True` if the password matched, `False` otherwise.
+
+ :param pwhash: a hashed string like returned by
+ :func:`generate_password_hash`
+ :param password: the plaintext password to compare against the hash
+ """
+ if pwhash.count('$') < 2:
+ return False
+ method, salt, hashval = pwhash.split('$', 2)
+ return safe_str_cmp(_hash_internal(method, salt, password), hashval)
+
+
+def safe_join(directory, filename):
+ """Safely join `directory` and `filename`. If this cannot be done,
+ this function returns ``None``.
+
+ :param directory: the base directory.
+ :param filename: the untrusted filename relative to that directory.
+ """
+ filename = posixpath.normpath(filename)
+ for sep in _os_alt_seps:
+ if sep in filename:
+ return None
+ if os.path.isabs(filename) or filename.startswith('../'):
+ return None
+ return os.path.join(directory, filename)
diff --git a/websdk/werkzeug/serving.py b/websdk/werkzeug/serving.py
new file mode 100644
index 0000000..48629f5
--- /dev/null
+++ b/websdk/werkzeug/serving.py
@@ -0,0 +1,614 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.serving
+ ~~~~~~~~~~~~~~~~
+
+ There are many ways to serve a WSGI application. While you're developing
+ it you usually don't want a full blown webserver like Apache but a simple
+ standalone one. From Python 2.5 onwards there is the `wsgiref`_ server in
+ the standard library. If you're using older versions of Python you can
+ download the package from the cheeseshop.
+
+ However there are some caveats. Sourcecode won't reload itself when
+ changed and each time you kill the server using ``^C`` you get an
+ `KeyboardInterrupt` error. While the latter is easy to solve the first
+ one can be a pain in the ass in some situations.
+
+ The easiest way is creating a small ``start-myproject.py`` that runs the
+ application::
+
+ #!/usr/bin/env python
+ # -*- coding: utf-8 -*-
+ from myproject import make_app
+ from werkzeug.serving import run_simple
+
+ app = make_app(...)
+ run_simple('localhost', 8080, app, use_reloader=True)
+
+ You can also pass it a `extra_files` keyword argument with a list of
+ additional files (like configuration files) you want to observe.
+
+ For bigger applications you should consider using `werkzeug.script`
+ instead of a simple start file.
+
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import socket
+import sys
+import time
+import thread
+import signal
+import subprocess
+from urllib import unquote
+from SocketServer import ThreadingMixIn, ForkingMixIn
+from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
+
+import werkzeug
+from werkzeug._internal import _log
+from werkzeug.exceptions import InternalServerError
+
+
+class WSGIRequestHandler(BaseHTTPRequestHandler, object):
+ """A request handler that implements WSGI dispatching."""
+
+ @property
+ def server_version(self):
+ return 'Werkzeug/' + werkzeug.__version__
+
+ def make_environ(self):
+ if '?' in self.path:
+ path_info, query = self.path.split('?', 1)
+ else:
+ path_info = self.path
+ query = ''
+
+ def shutdown_server():
+ self.server.shutdown_signal = True
+
+ url_scheme = self.server.ssl_context is None and 'http' or 'https'
+ environ = {
+ 'wsgi.version': (1, 0),
+ 'wsgi.url_scheme': url_scheme,
+ 'wsgi.input': self.rfile,
+ 'wsgi.errors': sys.stderr,
+ 'wsgi.multithread': self.server.multithread,
+ 'wsgi.multiprocess': self.server.multiprocess,
+ 'wsgi.run_once': False,
+ 'werkzeug.server.shutdown':
+ shutdown_server,
+ 'SERVER_SOFTWARE': self.server_version,
+ 'REQUEST_METHOD': self.command,
+ 'SCRIPT_NAME': '',
+ 'PATH_INFO': unquote(path_info),
+ 'QUERY_STRING': query,
+ 'CONTENT_TYPE': self.headers.get('Content-Type', ''),
+ 'CONTENT_LENGTH': self.headers.get('Content-Length', ''),
+ 'REMOTE_ADDR': self.client_address[0],
+ 'REMOTE_PORT': self.client_address[1],
+ 'SERVER_NAME': self.server.server_address[0],
+ 'SERVER_PORT': str(self.server.server_address[1]),
+ 'SERVER_PROTOCOL': self.request_version
+ }
+
+ for key, value in self.headers.items():
+ key = 'HTTP_' + key.upper().replace('-', '_')
+ if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
+ environ[key] = value
+
+ return environ
+
+ def run_wsgi(self):
+ app = self.server.app
+ environ = self.make_environ()
+ headers_set = []
+ headers_sent = []
+
+ def write(data):
+ assert headers_set, 'write() before start_response'
+ if not headers_sent:
+ status, response_headers = headers_sent[:] = headers_set
+ code, msg = status.split(None, 1)
+ self.send_response(int(code), msg)
+ header_keys = set()
+ for key, value in response_headers:
+ self.send_header(key, value)
+ key = key.lower()
+ header_keys.add(key)
+ if 'content-length' not in header_keys:
+ self.close_connection = True
+ self.send_header('Connection', 'close')
+ if 'server' not in header_keys:
+ self.send_header('Server', self.version_string())
+ if 'date' not in header_keys:
+ self.send_header('Date', self.date_time_string())
+ self.end_headers()
+
+ assert type(data) is str, 'applications must write bytes'
+ self.wfile.write(data)
+ self.wfile.flush()
+
+ def start_response(status, response_headers, exc_info=None):
+ if exc_info:
+ try:
+ if headers_sent:
+ raise exc_info[0], exc_info[1], exc_info[2]
+ finally:
+ exc_info = None
+ elif headers_set:
+ raise AssertionError('Headers already set')
+ headers_set[:] = [status, response_headers]
+ return write
+
+ def execute(app):
+ application_iter = app(environ, start_response)
+ try:
+ for data in application_iter:
+ write(data)
+ # make sure the headers are sent
+ if not headers_sent:
+ write('')
+ finally:
+ if hasattr(application_iter, 'close'):
+ application_iter.close()
+ application_iter = None
+
+ try:
+ execute(app)
+ except (socket.error, socket.timeout), e:
+ self.connection_dropped(e, environ)
+ except Exception:
+ if self.server.passthrough_errors:
+ raise
+ from werkzeug.debug.tbtools import get_current_traceback
+ traceback = get_current_traceback(ignore_system_exceptions=True)
+ try:
+ # if we haven't yet sent the headers but they are set
+ # we roll back to be able to set them again.
+ if not headers_sent:
+ del headers_set[:]
+ execute(InternalServerError())
+ except Exception:
+ pass
+ self.server.log('error', 'Error on request:\n%s',
+ traceback.plaintext)
+
+ def handle(self):
+ """Handles a request ignoring dropped connections."""
+ try:
+ rv = BaseHTTPRequestHandler.handle(self)
+ except (socket.error, socket.timeout), e:
+ self.connection_dropped(e)
+ except Exception:
+ if self.server.ssl_context is None or not is_ssl_error():
+ raise
+ if self.server.shutdown_signal:
+ self.initiate_shutdown()
+ return rv
+
+ def initiate_shutdown(self):
+ """A horrible, horrible way to kill the server for Python 2.6 and
+ later. It's the best we can do.
+ """
+ # reloader active
+ if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
+ os.kill(os.getpid(), signal.SIGKILL)
+ # python 2.7
+ self.server._BaseServer__shutdown_request = True
+ # python 2.6
+ self.server._BaseServer__serving = False
+
+ def connection_dropped(self, error, environ=None):
+ """Called if the connection was closed by the client. By default
+ nothing happens.
+ """
+
+ def handle_one_request(self):
+ """Handle a single HTTP request."""
+ self.raw_requestline = self.rfile.readline()
+ if not self.raw_requestline:
+ self.close_connection = 1
+ elif self.parse_request():
+ return self.run_wsgi()
+
+ def send_response(self, code, message=None):
+ """Send the response header and log the response code."""
+ self.log_request(code)
+ if message is None:
+ message = code in self.responses and self.responses[code][0] or ''
+ if self.request_version != 'HTTP/0.9':
+ self.wfile.write("%s %d %s\r\n" %
+ (self.protocol_version, code, message))
+
+ def version_string(self):
+ return BaseHTTPRequestHandler.version_string(self).strip()
+
+ def address_string(self):
+ return self.client_address[0]
+
+ def log_request(self, code='-', size='-'):
+ self.log('info', '"%s" %s %s', self.requestline, code, size)
+
+ def log_error(self, *args):
+ self.log('error', *args)
+
+ def log_message(self, format, *args):
+ self.log('info', format, *args)
+
+ def log(self, type, message, *args):
+ _log(type, '%s - - [%s] %s\n' % (self.address_string(),
+ self.log_date_time_string(),
+ message % args))
+
+
+#: backwards compatible name if someone is subclassing it
+BaseRequestHandler = WSGIRequestHandler
+
+
+def generate_adhoc_ssl_context():
+ """Generates an adhoc SSL context for the development server."""
+ from random import random
+ from OpenSSL import crypto, SSL
+
+ cert = crypto.X509()
+ cert.set_serial_number(int(random() * sys.maxint))
+ cert.gmtime_adj_notBefore(0)
+ cert.gmtime_adj_notAfter(60 * 60 * 24 * 365)
+
+ subject = cert.get_subject()
+ subject.CN = '*'
+ subject.O = 'Dummy Certificate'
+
+ issuer = cert.get_issuer()
+ issuer.CN = 'Untrusted Authority'
+ issuer.O = 'Self-Signed'
+
+ pkey = crypto.PKey()
+ pkey.generate_key(crypto.TYPE_RSA, 768)
+ cert.set_pubkey(pkey)
+ cert.sign(pkey, 'md5')
+
+ ctx = SSL.Context(SSL.SSLv23_METHOD)
+ ctx.use_privatekey(pkey)
+ ctx.use_certificate(cert)
+
+ return ctx
+
+
+def is_ssl_error(error=None):
+ """Checks if the given error (or the current one) is an SSL error."""
+ if error is None:
+ error = sys.exc_info()[1]
+ from OpenSSL import SSL
+ return isinstance(error, SSL.Error)
+
+
+class _SSLConnectionFix(object):
+ """Wrapper around SSL connection to provide a working makefile()."""
+
+ def __init__(self, con):
+ self._con = con
+
+ def makefile(self, mode, bufsize):
+ return socket._fileobject(self._con, mode, bufsize)
+
+ def __getattr__(self, attrib):
+ return getattr(self._con, attrib)
+
+
+def select_ip_version(host, port):
+ """Returns AF_INET4 or AF_INET6 depending on where to connect to."""
+ # disabled due to problems with current ipv6 implementations
+ # and various operating systems. Probably this code also is
+ # not supposed to work, but I can't come up with any other
+ # ways to implement this.
+ ##try:
+ ## info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
+ ## socket.SOCK_STREAM, 0,
+ ## socket.AI_PASSIVE)
+ ## if info:
+ ## return info[0][0]
+ ##except socket.gaierror:
+ ## pass
+ if ':' in host and hasattr(socket, 'AF_INET6'):
+ return socket.AF_INET6
+ return socket.AF_INET
+
+
+class BaseWSGIServer(HTTPServer, object):
+ """Simple single-threaded, single-process WSGI server."""
+ multithread = False
+ multiprocess = False
+ request_queue_size = 128
+
+ def __init__(self, host, port, app, handler=None,
+ passthrough_errors=False, ssl_context=None):
+ if handler is None:
+ handler = WSGIRequestHandler
+ self.address_family = select_ip_version(host, port)
+ HTTPServer.__init__(self, (host, int(port)), handler)
+ self.app = app
+ self.passthrough_errors = passthrough_errors
+ self.shutdown_signal = False
+
+ if ssl_context is not None:
+ try:
+ from OpenSSL import tsafe
+ except ImportError:
+ raise TypeError('SSL is not available if the OpenSSL '
+ 'library is not installed.')
+ if ssl_context == 'adhoc':
+ ssl_context = generate_adhoc_ssl_context()
+ self.socket = tsafe.Connection(ssl_context, self.socket)
+ self.ssl_context = ssl_context
+ else:
+ self.ssl_context = None
+
+ def log(self, type, message, *args):
+ _log(type, message, *args)
+
+ def serve_forever(self):
+ self.shutdown_signal = False
+ try:
+ HTTPServer.serve_forever(self)
+ except KeyboardInterrupt:
+ pass
+
+ def handle_error(self, request, client_address):
+ if self.passthrough_errors:
+ raise
+ else:
+ return HTTPServer.handle_error(self, request, client_address)
+
+ def get_request(self):
+ con, info = self.socket.accept()
+ if self.ssl_context is not None:
+ con = _SSLConnectionFix(con)
+ return con, info
+
+
+class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer):
+ """A WSGI server that does threading."""
+ multithread = True
+
+
+class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):
+ """A WSGI server that does forking."""
+ multiprocess = True
+
+ def __init__(self, host, port, app, processes=40, handler=None,
+ passthrough_errors=False, ssl_context=None):
+ BaseWSGIServer.__init__(self, host, port, app, handler,
+ passthrough_errors, ssl_context)
+ self.max_children = processes
+
+
+def make_server(host, port, app=None, threaded=False, processes=1,
+ request_handler=None, passthrough_errors=False,
+ ssl_context=None):
+ """Create a new server instance that is either threaded, or forks
+ or just processes one request after another.
+ """
+ if threaded and processes > 1:
+ raise ValueError("cannot have a multithreaded and "
+ "multi process server.")
+ elif threaded:
+ return ThreadedWSGIServer(host, port, app, request_handler,
+ passthrough_errors, ssl_context)
+ elif processes > 1:
+ return ForkingWSGIServer(host, port, app, processes, request_handler,
+ passthrough_errors, ssl_context)
+ else:
+ return BaseWSGIServer(host, port, app, request_handler,
+ passthrough_errors, ssl_context)
+
+
+def _iter_module_files():
+ for module in sys.modules.values():
+ filename = getattr(module, '__file__', None)
+ if filename:
+ old = None
+ while not os.path.isfile(filename):
+ old = filename
+ filename = os.path.dirname(filename)
+ if filename == old:
+ break
+ else:
+ if filename[-4:] in ('.pyc', '.pyo'):
+ filename = filename[:-1]
+ yield filename
+
+
+def _reloader_stat_loop(extra_files=None, interval=1):
+ """When this function is run from the main thread, it will force other
+ threads to exit when any modules currently loaded change.
+
+ Copyright notice. This function is based on the autoreload.py from
+ the CherryPy trac which originated from WSGIKit which is now dead.
+
+ :param extra_files: a list of additional files it should watch.
+ """
+ from itertools import chain
+ mtimes = {}
+ while 1:
+ for filename in chain(_iter_module_files(), extra_files or ()):
+ try:
+ mtime = os.stat(filename).st_mtime
+ except OSError:
+ continue
+
+ old_time = mtimes.get(filename)
+ if old_time is None:
+ mtimes[filename] = mtime
+ continue
+ elif mtime > old_time:
+ _log('info', ' * Detected change in %r, reloading' % filename)
+ sys.exit(3)
+ time.sleep(interval)
+
+
+def _reloader_inotify(extra_files=None, interval=None):
+ # Mutated by inotify loop when changes occur.
+ changed = [False]
+
+ # Setup inotify watches
+ from pyinotify import WatchManager, Notifier
+
+ # this API changed at one point, support both
+ try:
+ from pyinotify import EventsCodes as ec
+ ec.IN_ATTRIB
+ except (ImportError, AttributeError):
+ import pyinotify as ec
+
+ wm = WatchManager()
+ mask = ec.IN_DELETE_SELF | ec.IN_MOVE_SELF | ec.IN_MODIFY | ec.IN_ATTRIB
+
+ def signal_changed(event):
+ if changed[0]:
+ return
+ _log('info', ' * Detected change in %r, reloading' % event.path)
+ changed[:] = [True]
+
+ for fname in extra_files or ():
+ wm.add_watch(fname, mask, signal_changed)
+
+ # ... And now we wait...
+ notif = Notifier(wm)
+ try:
+ while not changed[0]:
+ # always reiterate through sys.modules, adding them
+ for fname in _iter_module_files():
+ wm.add_watch(fname, mask, signal_changed)
+ notif.process_events()
+ if notif.check_events(timeout=interval):
+ notif.read_events()
+ # TODO Set timeout to something small and check parent liveliness
+ finally:
+ notif.stop()
+ sys.exit(3)
+
+
+# currently we always use the stat loop reloader for the simple reason
+# that the inotify one does not respond to added files properly. Also
+# it's quite buggy and the API is a mess.
+reloader_loop = _reloader_stat_loop
+
+
+def restart_with_reloader():
+ """Spawn a new Python interpreter with the same arguments as this one,
+ but running the reloader thread.
+ """
+ while 1:
+ _log('info', ' * Restarting with reloader')
+ args = [sys.executable] + sys.argv
+ new_environ = os.environ.copy()
+ new_environ['WERKZEUG_RUN_MAIN'] = 'true'
+
+ # a weird bug on windows. sometimes unicode strings end up in the
+ # environment and subprocess.call does not like this, encode them
+ # to latin1 and continue.
+ if os.name == 'nt':
+ for key, value in new_environ.iteritems():
+ if isinstance(value, unicode):
+ new_environ[key] = value.encode('iso-8859-1')
+
+ exit_code = subprocess.call(args, env=new_environ)
+ if exit_code != 3:
+ return exit_code
+
+
+def run_with_reloader(main_func, extra_files=None, interval=1):
+ """Run the given function in an independent python interpreter."""
+ import signal
+ signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
+ if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
+ thread.start_new_thread(main_func, ())
+ try:
+ reloader_loop(extra_files, interval)
+ except KeyboardInterrupt:
+ return
+ try:
+ sys.exit(restart_with_reloader())
+ except KeyboardInterrupt:
+ pass
+
+
+def run_simple(hostname, port, application, use_reloader=False,
+ use_debugger=False, use_evalex=True,
+ extra_files=None, reloader_interval=1, threaded=False,
+ processes=1, request_handler=None, static_files=None,
+ passthrough_errors=False, ssl_context=None):
+ """Start an application using wsgiref and with an optional reloader. This
+ wraps `wsgiref` to fix the wrong default reporting of the multithreaded
+ WSGI variable and adds optional multithreading and fork support.
+
+ .. versionadded:: 0.5
+ `static_files` was added to simplify serving of static files as well
+ as `passthrough_errors`.
+
+ .. versionadded:: 0.6
+ support for SSL was added.
+
+ :param hostname: The host for the application. eg: ``'localhost'``
+ :param port: The port for the server. eg: ``8080``
+ :param application: the WSGI application to execute
+ :param use_reloader: should the server automatically restart the python
+ process if modules were changed?
+ :param use_debugger: should the werkzeug debugging system be used?
+ :param use_evalex: should the exception evaluation feature be enabled?
+ :param extra_files: a list of files the reloader should watch
+ additionally to the modules. For example configuration
+ files.
+ :param reloader_interval: the interval for the reloader in seconds.
+ :param threaded: should the process handle each request in a separate
+ thread?
+ :param processes: number of processes to spawn.
+ :param request_handler: optional parameter that can be used to replace
+ the default one. You can use this to replace it
+ with a different
+ :class:`~BaseHTTPServer.BaseHTTPRequestHandler`
+ subclass.
+ :param static_files: a dict of paths for static files. This works exactly
+ like :class:`SharedDataMiddleware`, it's actually
+ just wrapping the application in that middleware before
+ serving.
+ :param passthrough_errors: set this to `True` to disable the error catching.
+ This means that the server will die on errors but
+ it can be useful to hook debuggers in (pdb etc.)
+ :param ssl_context: an SSL context for the connection. Either an OpenSSL
+ context, the string ``'adhoc'`` if the server should
+ automatically create one, or `None` to disable SSL
+ (which is the default).
+ """
+ if use_debugger:
+ from werkzeug.debug import DebuggedApplication
+ application = DebuggedApplication(application, use_evalex)
+ if static_files:
+ from werkzeug.wsgi import SharedDataMiddleware
+ application = SharedDataMiddleware(application, static_files)
+
+ def inner():
+ make_server(hostname, port, application, threaded,
+ processes, request_handler,
+ passthrough_errors, ssl_context).serve_forever()
+
+ if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
+ display_hostname = hostname != '*' and hostname or 'localhost'
+ if ':' in display_hostname:
+ display_hostname = '[%s]' % display_hostname
+ _log('info', ' * Running on %s://%s:%d/', ssl_context is None
+ and 'http' or 'https', display_hostname, port)
+ if use_reloader:
+ # Create and destroy a socket so that any exceptions are raised before
+ # we spawn a separate Python interpreter and lose this ability.
+ address_family = select_ip_version(hostname, port)
+ test_socket = socket.socket(address_family, socket.SOCK_STREAM)
+ test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ test_socket.bind((hostname, port))
+ test_socket.close()
+ run_with_reloader(inner, extra_files, reloader_interval)
+ else:
+ inner()
diff --git a/websdk/werkzeug/templates.py b/websdk/werkzeug/templates.py
new file mode 100644
index 0000000..a9d60e9
--- /dev/null
+++ b/websdk/werkzeug/templates.py
@@ -0,0 +1,399 @@
+# -*- coding: utf-8 -*-
+r"""
+ werkzeug.templates
+ ~~~~~~~~~~~~~~~~~~
+
+ A minimal template engine.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD License.
+"""
+import sys
+import re
+import __builtin__ as builtins
+from compiler import ast, parse
+from compiler.pycodegen import ModuleCodeGenerator
+from tokenize import PseudoToken
+from werkzeug import urls, utils
+from werkzeug._internal import _decode_unicode
+from werkzeug.datastructures import MultiDict
+
+
+from warnings import warn
+warn(DeprecationWarning('werkzeug.templates is deprecated and '
+ 'will be removed in Werkzeug 1.0'))
+
+
+# Copyright notice: The `parse_data` method uses the string interpolation
+# algorithm by Ka-Ping Yee which originally was part of `Itpl20.py`_.
+#
+# .. _Itpl20.py: http://lfw.org/python/Itpl20.py
+
+
+token_re = re.compile('%s|%s(?s)' % (
+ r'[uU]?[rR]?("""|\'\'\')((?<!\\)\\\1|.)*?\1',
+ PseudoToken
+))
+directive_re = re.compile(r'(?<!\\)<%(?:(#)|(py(?:thon)?\b)|'
+ r'(?:\s*(\w+))\s*)(.*?)\s*%>\n?(?s)')
+escape_re = re.compile(r'\\\n|\\(\\|<%)')
+namestart_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
+undefined = type('UndefinedType', (object,), {
+ '__iter__': lambda x: iter(()),
+ '__repr__': lambda x: 'Undefined',
+ '__str__': lambda x: ''
+})()
+runtime_vars = frozenset(['Undefined', '__to_unicode', '__context',
+ '__write', '__write_many'])
+
+
+def call_stmt(func, args, lineno):
+ return ast.CallFunc(ast.Name(func, lineno=lineno),
+ args, lineno=lineno)
+
+
+def tokenize(source, filename):
+ escape = escape_re.sub
+ escape_repl = lambda m: m.group(1) or ''
+ lineno = 1
+ pos = 0
+
+ for match in directive_re.finditer(source):
+ start, end = match.span()
+ if start > pos:
+ data = source[pos:start]
+ yield lineno, 'data', escape(escape_repl, data)
+ lineno += data.count('\n')
+ is_comment, is_code, cmd, args = match.groups()
+ if is_code:
+ yield lineno, 'code', args
+ elif not is_comment:
+ yield lineno, 'cmd', (cmd, args)
+ lineno += source[start:end].count('\n')
+ pos = end
+
+ if pos < len(source):
+ yield lineno, 'data', escape(escape_repl, source[pos:])
+
+
+def transform(node, filename):
+ root = ast.Module(None, node, lineno=1)
+ nodes = [root]
+ while nodes:
+ node = nodes.pop()
+ node.filename = filename
+ if node.__class__ in (ast.Printnl, ast.Print):
+ node.dest = ast.Name('__context')
+ elif node.__class__ is ast.Const and isinstance(node.value, str):
+ try:
+ node.value.decode('ascii')
+ except UnicodeError:
+ node.value = node.value.decode('utf-8')
+ nodes.extend(node.getChildNodes())
+ return root
+
+
+class TemplateSyntaxError(SyntaxError):
+
+ def __init__(self, msg, filename, lineno):
+ from linecache import getline
+ l = getline(filename, lineno)
+ SyntaxError.__init__(self, msg, (filename, lineno, len(l) or 1, l))
+
+
+class Parser(object):
+
+ def __init__(self, gen, filename):
+ self.gen = gen
+ self.filename = filename
+ self.lineno = 1
+
+ def fail(self, msg):
+ raise TemplateSyntaxError(msg, self.filename, self.lineno)
+
+ def parse_python(self, expr, type='exec'):
+ if isinstance(expr, unicode):
+ expr = '\xef\xbb\xbf' + expr.encode('utf-8')
+ try:
+ node = parse(expr, type)
+ except SyntaxError, e:
+ raise TemplateSyntaxError(str(e), self.filename,
+ self.lineno + e.lineno - 1)
+ nodes = [node]
+ while nodes:
+ n = nodes.pop()
+ if hasattr(n, 'lineno'):
+ n.lineno = (n.lineno or 1) + self.lineno - 1
+ nodes.extend(n.getChildNodes())
+ return node.node
+
+ def parse(self, needle=()):
+ start_lineno = self.lineno
+ result = []
+ add = result.append
+ for self.lineno, token, value in self.gen:
+ if token == 'data':
+ add(self.parse_data(value))
+ elif token == 'code':
+ add(self.parse_code(value.splitlines()))
+ elif token == 'cmd':
+ name, args = value
+ if name in needle:
+ return name, args, ast.Stmt(result, lineno=start_lineno)
+ if name in ('for', 'while'):
+ add(self.parse_loop(args, name))
+ elif name == 'if':
+ add(self.parse_if(args))
+ else:
+ self.fail('unknown directive %s' % name)
+ if needle:
+ self.fail('unexpected end of template')
+ return ast.Stmt(result, lineno=start_lineno)
+
+ def parse_loop(self, args, type):
+ rv = self.parse_python('%s %s: pass' % (type, args), 'exec').nodes[0]
+ tag, value, rv.body = self.parse(('end' + type, 'else'))
+ if value:
+ self.fail('unexpected data after ' + tag)
+ if tag == 'else':
+ tag, value, rv.else_ = self.parse(('end' + type,))
+ if value:
+ self.fail('unexpected data after else')
+ return rv
+
+ def parse_if(self, args):
+ cond = self.parse_python('if %s: pass' % args).nodes[0]
+ tag, value, body = self.parse(('else', 'elif', 'endif'))
+ cond.tests[0] = (cond.tests[0][0], body)
+ while 1:
+ if tag == 'else':
+ if value:
+ self.fail('unexpected data after else')
+ tag, value, cond.else_ = self.parse(('endif',))
+ elif tag == 'elif':
+ expr = self.parse_python(value, 'eval')
+ tag, value, body = self.parse(('else', 'elif', 'endif'))
+ cond.tests.append((expr, body))
+ continue
+ break
+ if value:
+ self.fail('unexpected data after endif')
+ return cond
+
+ def parse_code(self, lines):
+ margin = sys.maxint
+ for line in lines[1:]:
+ content = len(line.lstrip())
+ if content:
+ indent = len(line) - content
+ margin = min(margin, indent)
+ if lines:
+ lines[0] = lines[0].lstrip()
+ if margin < sys.maxint:
+ for i in xrange(1, len(lines)):
+ lines[i] = lines[i][margin:]
+ while lines and not lines[-1]:
+ lines.pop()
+ while lines and not lines[0]:
+ lines.pop(0)
+ return self.parse_python('\n'.join(lines))
+
+ def parse_data(self, text):
+ start_lineno = lineno = self.lineno
+ pos = 0
+ end = len(text)
+ nodes = []
+
+ def match_or_fail(pos):
+ match = token_re.match(text, pos)
+ if match is None:
+ self.fail('invalid syntax')
+ return match.group().strip(), match.end()
+
+ def write_expr(code):
+ node = self.parse_python(code, 'eval')
+ nodes.append(call_stmt('__to_unicode', [node], lineno))
+ return code.count('\n')
+
+ def write_data(value):
+ if value:
+ nodes.append(ast.Const(value, lineno=lineno))
+ return value.count('\n')
+ return 0
+
+ while 1:
+ offset = text.find('$', pos)
+ if offset < 0:
+ break
+ next = text[offset + 1]
+
+ if next == '{':
+ lineno += write_data(text[pos:offset])
+ pos = offset + 2
+ level = 1
+ while level:
+ token, pos = match_or_fail(pos)
+ if token in ('{', '}'):
+ level += token == '{' and 1 or -1
+ lineno += write_expr(text[offset + 2:pos - 1])
+ elif next in namestart_chars:
+ lineno += write_data(text[pos:offset])
+ token, pos = match_or_fail(offset + 1)
+ while pos < end:
+ if text[pos] == '.' and pos + 1 < end and \
+ text[pos + 1] in namestart_chars:
+ token, pos = match_or_fail(pos + 1)
+ elif text[pos] in '([':
+ pos += 1
+ level = 1
+ while level:
+ token, pos = match_or_fail(pos)
+ if token in ('(', ')', '[', ']'):
+ level += token in '([' and 1 or -1
+ else:
+ break
+ lineno += write_expr(text[offset + 1:pos])
+ else:
+ lineno += write_data(text[pos:offset + 1])
+ pos = offset + 1 + (next == '$')
+ write_data(text[pos:])
+
+ return ast.Discard(call_stmt(len(nodes) == 1 and '__write' or
+ '__write_many', nodes, start_lineno),
+ lineno=start_lineno)
+
+
+class Context(object):
+
+ def __init__(self, namespace, charset, errors):
+ self.charset = charset
+ self.errors = errors
+ self._namespace = namespace
+ self._buffer = []
+ self._write = self._buffer.append
+ _extend = self._buffer.extend
+ self.runtime = dict(
+ Undefined=undefined,
+ __to_unicode=self.to_unicode,
+ __context=self,
+ __write=self._write,
+ __write_many=lambda *a: _extend(a)
+ )
+
+ def write(self, value):
+ self._write(self.to_unicode(value))
+
+ def to_unicode(self, value):
+ if isinstance(value, str):
+ return _decode_unicode(value, self.charset, self.errors)
+ return unicode(value)
+
+ def get_value(self, as_unicode=True):
+ rv = u''.join(self._buffer)
+ if not as_unicode:
+ return rv.encode(self.charset, self.errors)
+ return rv
+
+ def __getitem__(self, key, default=undefined):
+ try:
+ return self._namespace[key]
+ except KeyError:
+ return getattr(builtins, key, default)
+
+ def get(self, key, default=None):
+ return self.__getitem__(key, default)
+
+ def __setitem__(self, key, value):
+ self._namespace[key] = value
+
+ def __delitem__(self, key):
+ del self._namespace[key]
+
+
+class TemplateCodeGenerator(ModuleCodeGenerator):
+
+ def __init__(self, node, filename):
+ ModuleCodeGenerator.__init__(self, transform(node, filename))
+
+ def _nameOp(self, prefix, name):
+ if name in runtime_vars:
+ return self.emit(prefix + '_GLOBAL', name)
+ return ModuleCodeGenerator._nameOp(self, prefix, name)
+
+
+class Template(object):
+ """Represents a simple text based template. It's a good idea to load such
+ templates from files on the file system to get better debug output.
+ """
+
+ default_context = {
+ 'escape': utils.escape,
+ 'url_quote': urls.url_quote,
+ 'url_quote_plus': urls.url_quote_plus,
+ 'url_encode': urls.url_encode
+ }
+
+ def __init__(self, source, filename='<template>', charset='utf-8',
+ errors='strict', unicode_mode=True):
+ if isinstance(source, str):
+ source = _decode_unicode(source, charset, errors)
+ if isinstance(filename, unicode):
+ filename = filename.encode('utf-8')
+ node = Parser(tokenize(u'\n'.join(source.splitlines()),
+ filename), filename).parse()
+ self.code = TemplateCodeGenerator(node, filename).getCode()
+ self.filename = filename
+ self.charset = charset
+ self.errors = errors
+ self.unicode_mode = unicode_mode
+
+ @classmethod
+ def from_file(cls, file, charset='utf-8', errors='strict',
+ unicode_mode=True):
+ """Load a template from a file.
+
+ .. versionchanged:: 0.5
+ The encoding parameter was renamed to charset.
+
+ :param file: a filename or file object to load the template from.
+ :param charset: the charset of the template to load.
+ :param errors: the error behavior of the charset decoding.
+ :param unicode_mode: set to `False` to disable unicode mode.
+ :return: a template
+ """
+ close = False
+ f = file
+ if isinstance(file, basestring):
+ f = open(file, 'r')
+ close = True
+ try:
+ data = _decode_unicode(f.read(), charset, errors)
+ finally:
+ if close:
+ f.close()
+ return cls(data, getattr(f, 'name', '<template>'), charset,
+ errors, unicode_mode)
+
+ def render(self, *args, **kwargs):
+ """This function accepts either a dict or some keyword arguments which
+ will then be the context the template is evaluated in. The return
+ value will be the rendered template.
+
+ :param context: the function accepts the same arguments as the
+ :class:`dict` constructor.
+ :return: the rendered template as string
+ """
+ ns = self.default_context.copy()
+ if len(args) == 1 and isinstance(args[0], MultiDict):
+ ns.update(args[0].to_dict(flat=True))
+ else:
+ ns.update(dict(*args))
+ if kwargs:
+ ns.update(kwargs)
+ context = Context(ns, self.charset, self.errors)
+ exec self.code in context.runtime, context
+ return context.get_value(self.unicode_mode)
+
+ def substitute(self, *args, **kwargs):
+ """For API compatibility with `string.Template`."""
+ return self.render(*args, **kwargs)
diff --git a/websdk/werkzeug/test.py b/websdk/werkzeug/test.py
new file mode 100644
index 0000000..62153d4
--- /dev/null
+++ b/websdk/werkzeug/test.py
@@ -0,0 +1,843 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.test
+ ~~~~~~~~~~~~~
+
+ This module implements a client to WSGI applications for testing.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+import urlparse
+import mimetypes
+from time import time
+from random import random
+from itertools import chain
+from tempfile import TemporaryFile
+from cStringIO import StringIO
+from cookielib import CookieJar
+from urllib2 import Request as U2Request
+
+from werkzeug._internal import _empty_stream, _get_environ
+from werkzeug.wrappers import BaseRequest
+from werkzeug.urls import url_encode, url_fix, iri_to_uri, _unquote
+from werkzeug.wsgi import get_host, get_current_url, ClosingIterator
+from werkzeug.utils import dump_cookie
+from werkzeug.datastructures import FileMultiDict, MultiDict, \
+ CombinedMultiDict, Headers, FileStorage
+
+
+def stream_encode_multipart(values, use_tempfile=True, threshold=1024 * 500,
+ boundary=None, charset='utf-8'):
+ """Encode a dict of values (either strings or file descriptors or
+ :class:`FileStorage` objects.) into a multipart encoded string stored
+ in a file descriptor.
+ """
+ if boundary is None:
+ boundary = '---------------WerkzeugFormPart_%s%s' % (time(), random())
+ _closure = [StringIO(), 0, False]
+
+ if use_tempfile:
+ def write(string):
+ stream, total_length, on_disk = _closure
+ if on_disk:
+ stream.write(string)
+ else:
+ length = len(string)
+ if length + _closure[1] <= threshold:
+ stream.write(string)
+ else:
+ new_stream = TemporaryFile('wb+')
+ new_stream.write(stream.getvalue())
+ new_stream.write(string)
+ _closure[0] = new_stream
+ _closure[2] = True
+ _closure[1] = total_length + length
+ else:
+ write = _closure[0].write
+
+ if not isinstance(values, MultiDict):
+ values = MultiDict(values)
+
+ for key, values in values.iterlists():
+ for value in values:
+ write('--%s\r\nContent-Disposition: form-data; name="%s"' %
+ (boundary, key))
+ reader = getattr(value, 'read', None)
+ if reader is not None:
+ filename = getattr(value, 'filename',
+ getattr(value, 'name', None))
+ content_type = getattr(value, 'content_type', None)
+ if content_type is None:
+ content_type = filename and \
+ mimetypes.guess_type(filename)[0] or \
+ 'application/octet-stream'
+ if filename is not None:
+ write('; filename="%s"\r\n' % filename)
+ else:
+ write('\r\n')
+ write('Content-Type: %s\r\n\r\n' % content_type)
+ while 1:
+ chunk = reader(16384)
+ if not chunk:
+ break
+ write(chunk)
+ else:
+ if isinstance(value, unicode):
+ value = value.encode(charset)
+ write('\r\n\r\n' + str(value))
+ write('\r\n')
+ write('--%s--\r\n' % boundary)
+
+ length = int(_closure[0].tell())
+ _closure[0].seek(0)
+ return _closure[0], length, boundary
+
+
+def encode_multipart(values, boundary=None, charset='utf-8'):
+ """Like `stream_encode_multipart` but returns a tuple in the form
+ (``boundary``, ``data``) where data is a bytestring.
+ """
+ stream, length, boundary = stream_encode_multipart(
+ values, use_tempfile=False, boundary=boundary, charset=charset)
+ return boundary, stream.read()
+
+
+def File(fd, filename=None, mimetype=None):
+ """Backwards compat."""
+ from warnings import warn
+ warn(DeprecationWarning('werkzeug.test.File is deprecated, use the '
+ 'EnvironBuilder or FileStorage instead'))
+ return FileStorage(fd, filename=filename, content_type=mimetype)
+
+
+class _TestCookieHeaders(object):
+ """A headers adapter for cookielib
+ """
+
+ def __init__(self, headers):
+ self.headers = headers
+
+ def getheaders(self, name):
+ headers = []
+ name = name.lower()
+ for k, v in self.headers:
+ if k.lower() == name:
+ headers.append(v)
+ return headers
+
+
+class _TestCookieResponse(object):
+ """Something that looks like a httplib.HTTPResponse, but is actually just an
+ adapter for our test responses to make them available for cookielib.
+ """
+
+ def __init__(self, headers):
+ self.headers = _TestCookieHeaders(headers)
+
+ def info(self):
+ return self.headers
+
+
+class _TestCookieJar(CookieJar):
+ """A cookielib.CookieJar modified to inject and read cookie headers from
+ and to wsgi environments, and wsgi application responses.
+ """
+
+ def inject_wsgi(self, environ):
+ """Inject the cookies as client headers into the server's wsgi
+ environment.
+ """
+ cvals = []
+ for cookie in self:
+ cvals.append('%s=%s' % (cookie.name, cookie.value))
+ if cvals:
+ environ['HTTP_COOKIE'] = '; '.join(cvals)
+
+ def extract_wsgi(self, environ, headers):
+ """Extract the server's set-cookie headers as cookies into the
+ cookie jar.
+ """
+ self.extract_cookies(
+ _TestCookieResponse(headers),
+ U2Request(get_current_url(environ)),
+ )
+
+
+def _iter_data(data):
+ """Iterates over a dict or multidict yielding all keys and values.
+ This is used to iterate over the data passed to the
+ :class:`EnvironBuilder`.
+ """
+ if isinstance(data, MultiDict):
+ for key, values in data.iterlists():
+ for value in values:
+ yield key, value
+ else:
+ for key, values in data.iteritems():
+ if isinstance(values, list):
+ for value in values:
+ yield key, value
+ else:
+ yield key, values
+
+
+class EnvironBuilder(object):
+ """This class can be used to conveniently create a WSGI environment
+ for testing purposes. It can be used to quickly create WSGI environments
+ or request objects from arbitrary data.
+
+ The signature of this class is also used in some other places as of
+ Werkzeug 0.5 (:func:`create_environ`, :meth:`BaseResponse.from_values`,
+ :meth:`Client.open`). Because of this most of the functionality is
+ available through the constructor alone.
+
+ Files and regular form data can be manipulated independently of each
+ other with the :attr:`form` and :attr:`files` attributes, but are
+ passed with the same argument to the constructor: `data`.
+
+ `data` can be any of these values:
+
+ - a `str`: If it's a string it is converted into a :attr:`input_stream`,
+ the :attr:`content_length` is set and you have to provide a
+ :attr:`content_type`.
+ - a `dict`: If it's a dict the keys have to be strings and the values
+ any of the following objects:
+
+ - a :class:`file`-like object. These are converted into
+ :class:`FileStorage` objects automatically.
+ - a tuple. The :meth:`~FileMultiDict.add_file` method is called
+ with the tuple items as positional arguments.
+
+ .. versionadded:: 0.6
+ `path` and `base_url` can now be unicode strings that are encoded using
+ the :func:`iri_to_uri` function.
+
+ :param path: the path of the request. In the WSGI environment this will
+ end up as `PATH_INFO`. If the `query_string` is not defined
+ and there is a question mark in the `path` everything after
+ it is used as query string.
+ :param base_url: the base URL is a URL that is used to extract the WSGI
+ URL scheme, host (server name + server port) and the
+ script root (`SCRIPT_NAME`).
+ :param query_string: an optional string or dict with URL parameters.
+ :param method: the HTTP method to use, defaults to `GET`.
+ :param input_stream: an optional input stream. Do not specify this and
+ `data`. As soon as an input stream is set you can't
+ modify :attr:`args` and :attr:`files` unless you
+ set the :attr:`input_stream` to `None` again.
+ :param content_type: The content type for the request. As of 0.5 you
+ don't have to provide this when specifying files
+ and form data via `data`.
+ :param content_length: The content length for the request. You don't
+ have to specify this when providing data via
+ `data`.
+ :param errors_stream: an optional error stream that is used for
+ `wsgi.errors`. Defaults to :data:`stderr`.
+ :param multithread: controls `wsgi.multithread`. Defaults to `False`.
+ :param multiprocess: controls `wsgi.multiprocess`. Defaults to `False`.
+ :param run_once: controls `wsgi.run_once`. Defaults to `False`.
+ :param headers: an optional list or :class:`Headers` object of headers.
+ :param data: a string or dict of form data. See explanation above.
+ :param environ_base: an optional dict of environment defaults.
+ :param environ_overrides: an optional dict of environment overrides.
+ :param charset: the charset used to encode unicode data.
+ """
+
+ #: the server protocol to use. defaults to HTTP/1.1
+ server_protocol = 'HTTP/1.1'
+
+ #: the wsgi version to use. defaults to (1, 0)
+ wsgi_version = (1, 0)
+
+ #: the default request class for :meth:`get_request`
+ request_class = BaseRequest
+
+ def __init__(self, path='/', base_url=None, query_string=None,
+ method='GET', input_stream=None, content_type=None,
+ content_length=None, errors_stream=None, multithread=False,
+ multiprocess=False, run_once=False, headers=None, data=None,
+ environ_base=None, environ_overrides=None, charset='utf-8'):
+ if query_string is None and '?' in path:
+ path, query_string = path.split('?', 1)
+ self.charset = charset
+ if isinstance(path, unicode):
+ path = iri_to_uri(path, charset)
+ self.path = path
+ if base_url is not None:
+ if isinstance(base_url, unicode):
+ base_url = iri_to_uri(base_url, charset)
+ else:
+ base_url = url_fix(base_url, charset)
+ self.base_url = base_url
+ if isinstance(query_string, basestring):
+ self.query_string = query_string
+ else:
+ if query_string is None:
+ query_string = MultiDict()
+ elif not isinstance(query_string, MultiDict):
+ query_string = MultiDict(query_string)
+ self.args = query_string
+ self.method = method
+ if headers is None:
+ headers = Headers()
+ elif not isinstance(headers, Headers):
+ headers = Headers(headers)
+ self.headers = headers
+ self.content_type = content_type
+ if errors_stream is None:
+ errors_stream = sys.stderr
+ self.errors_stream = errors_stream
+ self.multithread = multithread
+ self.multiprocess = multiprocess
+ self.run_once = run_once
+ self.environ_base = environ_base
+ self.environ_overrides = environ_overrides
+ self.input_stream = input_stream
+ self.content_length = content_length
+ self.closed = False
+
+ if data:
+ if input_stream is not None:
+ raise TypeError('can\'t provide input stream and data')
+ if isinstance(data, basestring):
+ self.input_stream = StringIO(data)
+ if self.content_length is None:
+ self.content_length = len(data)
+ else:
+ for key, value in _iter_data(data):
+ if isinstance(value, (tuple, dict)) or \
+ hasattr(value, 'read'):
+ self._add_file_from_data(key, value)
+ else:
+ self.form.setlistdefault(key).append(value)
+
+ def _add_file_from_data(self, key, value):
+ """Called in the EnvironBuilder to add files from the data dict."""
+ if isinstance(value, tuple):
+ self.files.add_file(key, *value)
+ elif isinstance(value, dict):
+ from warnings import warn
+ warn(DeprecationWarning('it\'s no longer possible to pass dicts '
+ 'as `data`. Use tuples or FileStorage '
+ 'objects instead'), stacklevel=2)
+ value = dict(value)
+ mimetype = value.pop('mimetype', None)
+ if mimetype is not None:
+ value['content_type'] = mimetype
+ self.files.add_file(key, **value)
+ else:
+ self.files.add_file(key, value)
+
+ def _get_base_url(self):
+ return urlparse.urlunsplit((self.url_scheme, self.host,
+ self.script_root, '', '')).rstrip('/') + '/'
+
+ def _set_base_url(self, value):
+ if value is None:
+ scheme = 'http'
+ netloc = 'localhost'
+ scheme = 'http'
+ script_root = ''
+ else:
+ scheme, netloc, script_root, qs, anchor = urlparse.urlsplit(value)
+ if qs or anchor:
+ raise ValueError('base url must not contain a query string '
+ 'or fragment')
+ self.script_root = script_root.rstrip('/')
+ self.host = netloc
+ self.url_scheme = scheme
+
+ base_url = property(_get_base_url, _set_base_url, doc='''
+ The base URL is a URL that is used to extract the WSGI
+ URL scheme, host (server name + server port) and the
+ script root (`SCRIPT_NAME`).''')
+ del _get_base_url, _set_base_url
+
+ def _get_content_type(self):
+ ct = self.headers.get('Content-Type')
+ if ct is None and not self._input_stream:
+ if self.method in ('POST', 'PUT', 'PATCH'):
+ if self._files:
+ return 'multipart/form-data'
+ return 'application/x-www-form-urlencoded'
+ return None
+ return ct
+
+ def _set_content_type(self, value):
+ if value is None:
+ self.headers.pop('Content-Type', None)
+ else:
+ self.headers['Content-Type'] = value
+
+ content_type = property(_get_content_type, _set_content_type, doc='''
+ The content type for the request. Reflected from and to the
+ :attr:`headers`. Do not set if you set :attr:`files` or
+ :attr:`form` for auto detection.''')
+ del _get_content_type, _set_content_type
+
+ def _get_content_length(self):
+ return self.headers.get('Content-Length', type=int)
+
+ def _set_content_length(self, value):
+ if value is None:
+ self.headers.pop('Content-Length', None)
+ else:
+ self.headers['Content-Length'] = str(value)
+
+ content_length = property(_get_content_length, _set_content_length, doc='''
+ The content length as integer. Reflected from and to the
+ :attr:`headers`. Do not set if you set :attr:`files` or
+ :attr:`form` for auto detection.''')
+ del _get_content_length, _set_content_length
+
+ def form_property(name, storage, doc):
+ key = '_' + name
+ def getter(self):
+ if self._input_stream is not None:
+ raise AttributeError('an input stream is defined')
+ rv = getattr(self, key)
+ if rv is None:
+ rv = storage()
+ setattr(self, key, rv)
+ return rv
+ def setter(self, value):
+ self._input_stream = None
+ setattr(self, key, value)
+ return property(getter, setter, doc)
+
+ form = form_property('form', MultiDict, doc='''
+ A :class:`MultiDict` of form values.''')
+ files = form_property('files', FileMultiDict, doc='''
+ A :class:`FileMultiDict` of uploaded files. You can use the
+ :meth:`~FileMultiDict.add_file` method to add new files to the
+ dict.''')
+ del form_property
+
+ def _get_input_stream(self):
+ return self._input_stream
+
+ def _set_input_stream(self, value):
+ self._input_stream = value
+ self._form = self._files = None
+
+ input_stream = property(_get_input_stream, _set_input_stream, doc='''
+ An optional input stream. If you set this it will clear
+ :attr:`form` and :attr:`files`.''')
+ del _get_input_stream, _set_input_stream
+
+ def _get_query_string(self):
+ if self._query_string is None:
+ if self._args is not None:
+ return url_encode(self._args, charset=self.charset)
+ return ''
+ return self._query_string
+
+ def _set_query_string(self, value):
+ self._query_string = value
+ self._args = None
+
+ query_string = property(_get_query_string, _set_query_string, doc='''
+ The query string. If you set this to a string :attr:`args` will
+ no longer be available.''')
+ del _get_query_string, _set_query_string
+
+ def _get_args(self):
+ if self._query_string is not None:
+ raise AttributeError('a query string is defined')
+ if self._args is None:
+ self._args = MultiDict()
+ return self._args
+
+ def _set_args(self, value):
+ self._query_string = None
+ self._args = value
+
+ args = property(_get_args, _set_args, doc='''
+ The URL arguments as :class:`MultiDict`.''')
+ del _get_args, _set_args
+
+ @property
+ def server_name(self):
+ """The server name (read-only, use :attr:`host` to set)"""
+ return self.host.split(':', 1)[0]
+
+ @property
+ def server_port(self):
+ """The server port as integer (read-only, use :attr:`host` to set)"""
+ pieces = self.host.split(':', 1)
+ if len(pieces) == 2 and pieces[1].isdigit():
+ return int(pieces[1])
+ elif self.url_scheme == 'https':
+ return 443
+ return 80
+
+ def __del__(self):
+ self.close()
+
+ def close(self):
+ """Closes all files. If you put real :class:`file` objects into the
+ :attr:`files` dict you can call this method to automatically close
+ them all in one go.
+ """
+ if self.closed:
+ return
+ try:
+ files = self.files.itervalues()
+ except AttributeError:
+ files = ()
+ for f in files:
+ try:
+ f.close()
+ except Exception, e:
+ pass
+ self.closed = True
+
+ def get_environ(self):
+ """Return the built environ."""
+ input_stream = self.input_stream
+ content_length = self.content_length
+ content_type = self.content_type
+
+ if input_stream is not None:
+ start_pos = input_stream.tell()
+ input_stream.seek(0, 2)
+ end_pos = input_stream.tell()
+ input_stream.seek(start_pos)
+ content_length = end_pos - start_pos
+ elif content_type == 'multipart/form-data':
+ values = CombinedMultiDict([self.form, self.files])
+ input_stream, content_length, boundary = \
+ stream_encode_multipart(values, charset=self.charset)
+ content_type += '; boundary="%s"' % boundary
+ elif content_type == 'application/x-www-form-urlencoded':
+ values = url_encode(self.form, charset=self.charset)
+ content_length = len(values)
+ input_stream = StringIO(values)
+ else:
+ input_stream = _empty_stream
+
+ result = {}
+ if self.environ_base:
+ result.update(self.environ_base)
+
+ def _path_encode(x):
+ if isinstance(x, unicode):
+ x = x.encode(self.charset)
+ return _unquote(x)
+
+ result.update({
+ 'REQUEST_METHOD': self.method,
+ 'SCRIPT_NAME': _path_encode(self.script_root),
+ 'PATH_INFO': _path_encode(self.path),
+ 'QUERY_STRING': self.query_string,
+ 'SERVER_NAME': self.server_name,
+ 'SERVER_PORT': str(self.server_port),
+ 'HTTP_HOST': self.host,
+ 'SERVER_PROTOCOL': self.server_protocol,
+ 'CONTENT_TYPE': content_type or '',
+ 'CONTENT_LENGTH': str(content_length or '0'),
+ 'wsgi.version': self.wsgi_version,
+ 'wsgi.url_scheme': self.url_scheme,
+ 'wsgi.input': input_stream,
+ 'wsgi.errors': self.errors_stream,
+ 'wsgi.multithread': self.multithread,
+ 'wsgi.multiprocess': self.multiprocess,
+ 'wsgi.run_once': self.run_once
+ })
+ for key, value in self.headers.to_list(self.charset):
+ result['HTTP_%s' % key.upper().replace('-', '_')] = value
+ if self.environ_overrides:
+ result.update(self.environ_overrides)
+ return result
+
+ def get_request(self, cls=None):
+ """Returns a request with the data. If the request class is not
+ specified :attr:`request_class` is used.
+
+ :param cls: The request wrapper to use.
+ """
+ if cls is None:
+ cls = self.request_class
+ return cls(self.get_environ())
+
+
+class ClientRedirectError(Exception):
+ """
+ If a redirect loop is detected when using follow_redirects=True with
+ the :cls:`Client`, then this exception is raised.
+ """
+
+
+class Client(object):
+ """This class allows to send requests to a wrapped application.
+
+ The response wrapper can be a class or factory function that takes
+ three arguments: app_iter, status and headers. The default response
+ wrapper just returns a tuple.
+
+ Example::
+
+ class ClientResponse(BaseResponse):
+ ...
+
+ client = Client(MyApplication(), response_wrapper=ClientResponse)
+
+ The use_cookies parameter indicates whether cookies should be stored and
+ sent for subsequent requests. This is True by default, but passing False
+ will disable this behaviour.
+
+ If you want to request some subdomain of your application you may set
+ `allow_subdomain_redirects` to `True` as if not no external redirects
+ are allowed.
+
+ .. versionadded:: 0.5
+ `use_cookies` is new in this version. Older versions did not provide
+ builtin cookie support.
+ """
+
+ def __init__(self, application, response_wrapper=None, use_cookies=True,
+ allow_subdomain_redirects=False):
+ self.application = application
+ if response_wrapper is None:
+ response_wrapper = lambda a, s, h: (a, s, h)
+ self.response_wrapper = response_wrapper
+ if use_cookies:
+ self.cookie_jar = _TestCookieJar()
+ else:
+ self.cookie_jar = None
+ self.redirect_client = None
+ self.allow_subdomain_redirects = allow_subdomain_redirects
+
+ def set_cookie(self, server_name, key, value='', max_age=None,
+ expires=None, path='/', domain=None, secure=None,
+ httponly=False, charset='utf-8'):
+ """Sets a cookie in the client's cookie jar. The server name
+ is required and has to match the one that is also passed to
+ the open call.
+ """
+ assert self.cookie_jar is not None, 'cookies disabled'
+ header = dump_cookie(key, value, max_age, expires, path, domain,
+ secure, httponly, charset)
+ environ = create_environ(path, base_url='http://' + server_name)
+ headers = [('Set-Cookie', header)]
+ self.cookie_jar.extract_wsgi(environ, headers)
+
+ def delete_cookie(self, server_name, key, path='/', domain=None):
+ """Deletes a cookie in the test client."""
+ self.set_cookie(server_name, key, expires=0, max_age=0,
+ path=path, domain=domain)
+
+ def open(self, *args, **kwargs):
+ """Takes the same arguments as the :class:`EnvironBuilder` class with
+ some additions: You can provide a :class:`EnvironBuilder` or a WSGI
+ environment as only argument instead of the :class:`EnvironBuilder`
+ arguments and two optional keyword arguments (`as_tuple`, `buffered`)
+ that change the type of the return value or the way the application is
+ executed.
+
+ .. versionchanged:: 0.5
+ If a dict is provided as file in the dict for the `data` parameter
+ the content type has to be called `content_type` now instead of
+ `mimetype`. This change was made for consistency with
+ :class:`werkzeug.FileWrapper`.
+
+ The `follow_redirects` parameter was added to :func:`open`.
+
+ Additional parameters:
+
+ :param as_tuple: Returns a tuple in the form ``(environ, result)``
+ :param buffered: Set this to True to buffer the application run.
+ This will automatically close the application for
+ you as well.
+ :param follow_redirects: Set this to True if the `Client` should
+ follow HTTP redirects.
+ """
+ as_tuple = kwargs.pop('as_tuple', False)
+ buffered = kwargs.pop('buffered', False)
+ follow_redirects = kwargs.pop('follow_redirects', False)
+ environ = None
+ if not kwargs and len(args) == 1:
+ if isinstance(args[0], EnvironBuilder):
+ environ = args[0].get_environ()
+ elif isinstance(args[0], dict):
+ environ = args[0]
+ if environ is None:
+ builder = EnvironBuilder(*args, **kwargs)
+ try:
+ environ = builder.get_environ()
+ finally:
+ builder.close()
+
+ if self.cookie_jar is not None:
+ self.cookie_jar.inject_wsgi(environ)
+ rv = run_wsgi_app(self.application, environ, buffered=buffered)
+ if self.cookie_jar is not None:
+ self.cookie_jar.extract_wsgi(environ, rv[2])
+
+ # handle redirects
+ redirect_chain = []
+ status_code = int(rv[1].split(None, 1)[0])
+ while status_code in (301, 302, 303, 305, 307) and follow_redirects:
+ if not self.redirect_client:
+ # assume that we're not using the user defined response wrapper
+ # so that we don't need any ugly hacks to get the status
+ # code from the response.
+ self.redirect_client = Client(self.application)
+ self.redirect_client.cookie_jar = self.cookie_jar
+
+ redirect = dict(rv[2])['Location']
+
+ scheme, netloc, script_root, qs, anchor = urlparse.urlsplit(redirect)
+ base_url = urlparse.urlunsplit((scheme, netloc, '', '', '')).rstrip('/') + '/'
+
+ cur_server_name = netloc.split(':', 1)[0].split('.')
+ real_server_name = get_host(environ).split(':', 1)[0].split('.')
+
+ if self.allow_subdomain_redirects:
+ allowed = cur_server_name[-len(real_server_name):] == real_server_name
+ else:
+ allowed = cur_server_name == real_server_name
+
+ if not allowed:
+ raise RuntimeError('%r does not support redirect to '
+ 'external targets' % self.__class__)
+
+ redirect_chain.append((redirect, status_code))
+
+ # the redirect request should be a new request, and not be based on
+ # the old request
+
+ redirect_kwargs = {
+ 'path': script_root,
+ 'base_url': base_url,
+ 'query_string': qs,
+ 'as_tuple': True,
+ 'buffered': buffered,
+ 'follow_redirects': False,
+ }
+ environ, rv = self.redirect_client.open(**redirect_kwargs)
+ status_code = int(rv[1].split(None, 1)[0])
+
+ # Prevent loops
+ if redirect_chain[-1] in redirect_chain[:-1]:
+ raise ClientRedirectError("loop detected")
+
+ response = self.response_wrapper(*rv)
+ if as_tuple:
+ return environ, response
+ return response
+
+ def get(self, *args, **kw):
+ """Like open but method is enforced to GET."""
+ kw['method'] = 'GET'
+ return self.open(*args, **kw)
+
+ def patch(self, *args, **kw):
+ """Like open but method is enforced to PATCH."""
+ kw['method'] = 'PATCH'
+ return self.open(*args, **kw)
+
+ def post(self, *args, **kw):
+ """Like open but method is enforced to POST."""
+ kw['method'] = 'POST'
+ return self.open(*args, **kw)
+
+ def head(self, *args, **kw):
+ """Like open but method is enforced to HEAD."""
+ kw['method'] = 'HEAD'
+ return self.open(*args, **kw)
+
+ def put(self, *args, **kw):
+ """Like open but method is enforced to PUT."""
+ kw['method'] = 'PUT'
+ return self.open(*args, **kw)
+
+ def delete(self, *args, **kw):
+ """Like open but method is enforced to DELETE."""
+ kw['method'] = 'DELETE'
+ return self.open(*args, **kw)
+
+ def __repr__(self):
+ return '<%s %r>' % (
+ self.__class__.__name__,
+ self.application
+ )
+
+
+def create_environ(*args, **kwargs):
+ """Create a new WSGI environ dict based on the values passed. The first
+ parameter should be the path of the request which defaults to '/'. The
+ second one can either be an absolute path (in that case the host is
+ localhost:80) or a full path to the request with scheme, netloc port and
+ the path to the script.
+
+ This accepts the same arguments as the :class:`EnvironBuilder`
+ constructor.
+
+ .. versionchanged:: 0.5
+ This function is now a thin wrapper over :class:`EnvironBuilder` which
+ was added in 0.5. The `headers`, `environ_base`, `environ_overrides`
+ and `charset` parameters were added.
+ """
+ builder = EnvironBuilder(*args, **kwargs)
+ try:
+ return builder.get_environ()
+ finally:
+ builder.close()
+
+
+def run_wsgi_app(app, environ, buffered=False):
+ """Return a tuple in the form (app_iter, status, headers) of the
+ application output. This works best if you pass it an application that
+ returns an iterator all the time.
+
+ Sometimes applications may use the `write()` callable returned
+ by the `start_response` function. This tries to resolve such edge
+ cases automatically. But if you don't get the expected output you
+ should set `buffered` to `True` which enforces buffering.
+
+ If passed an invalid WSGI application the behavior of this function is
+ undefined. Never pass non-conforming WSGI applications to this function.
+
+ :param app: the application to execute.
+ :param buffered: set to `True` to enforce buffering.
+ :return: tuple in the form ``(app_iter, status, headers)``
+ """
+ environ = _get_environ(environ)
+ response = []
+ buffer = []
+
+ def start_response(status, headers, exc_info=None):
+ if exc_info is not None:
+ raise exc_info[0], exc_info[1], exc_info[2]
+ response[:] = [status, headers]
+ return buffer.append
+
+ app_iter = app(environ, start_response)
+
+ # when buffering we emit the close call early and convert the
+ # application iterator into a regular list
+ if buffered:
+ close_func = getattr(app_iter, 'close', None)
+ try:
+ app_iter = list(app_iter)
+ finally:
+ if close_func is not None:
+ close_func()
+
+ # otherwise we iterate the application iter until we have
+ # a response, chain the already received data with the already
+ # collected data and wrap it in a new `ClosingIterator` if
+ # we have a close callable.
+ else:
+ while not response:
+ buffer.append(app_iter.next())
+ if buffer:
+ close_func = getattr(app_iter, 'close', None)
+ app_iter = chain(buffer, app_iter)
+ if close_func is not None:
+ app_iter = ClosingIterator(app_iter, close_func)
+
+ return app_iter, response[0], response[1]
diff --git a/websdk/werkzeug/testapp.py b/websdk/werkzeug/testapp.py
new file mode 100644
index 0000000..ff660af
--- /dev/null
+++ b/websdk/werkzeug/testapp.py
@@ -0,0 +1,230 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testapp
+ ~~~~~~~~~~~~~~~~
+
+ Provide a small test application that can be used to test a WSGI server
+ and check it for WSGI compliance.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+import werkzeug
+from textwrap import wrap
+from werkzeug.wrappers import BaseRequest as Request, BaseResponse as Response
+from werkzeug.utils import escape
+
+
+logo = Response('''R0lGODlhoACgAOMIAAEDACwpAEpCAGdgAJaKAM28AOnVAP3rAP/////////
+//////////////////////yH5BAEKAAgALAAAAACgAKAAAAT+EMlJq704680R+F0ojmRpnuj0rWnrv
+nB8rbRs33gu0bzu/0AObxgsGn3D5HHJbCUFyqZ0ukkSDlAidctNFg7gbI9LZlrBaHGtzAae0eloe25
+7w9EDOX2fst/xenyCIn5/gFqDiVVDV4aGeYiKkhSFjnCQY5OTlZaXgZp8nJ2ekaB0SQOjqphrpnOiq
+ncEn65UsLGytLVmQ6m4sQazpbtLqL/HwpnER8bHyLrLOc3Oz8PRONPU1crXN9na263dMt/g4SzjMeX
+m5yDpLqgG7OzJ4u8lT/P69ej3JPn69kHzN2OIAHkB9RUYSFCFQYQJFTIkCDBiwoXWGnowaLEjRm7+G
+p9A7Hhx4rUkAUaSLJlxHMqVMD/aSycSZkyTplCqtGnRAM5NQ1Ly5OmzZc6gO4d6DGAUKA+hSocWYAo
+SlM6oUWX2O/o0KdaVU5vuSQLAa0ADwQgMEMB2AIECZhVSnTno6spgbtXmHcBUrQACcc2FrTrWS8wAf
+78cMFBgwIBgbN+qvTt3ayikRBk7BoyGAGABAdYyfdzRQGV3l4coxrqQ84GpUBmrdR3xNIDUPAKDBSA
+ADIGDhhqTZIWaDcrVX8EsbNzbkvCOxG8bN5w8ly9H8jyTJHC6DFndQydbguh2e/ctZJFXRxMAqqPVA
+tQH5E64SPr1f0zz7sQYjAHg0In+JQ11+N2B0XXBeeYZgBZFx4tqBToiTCPv0YBgQv8JqA6BEf6RhXx
+w1ENhRBnWV8ctEX4Ul2zc3aVGcQNC2KElyTDYyYUWvShdjDyMOGMuFjqnII45aogPhz/CodUHFwaDx
+lTgsaOjNyhGWJQd+lFoAGk8ObghI0kawg+EV5blH3dr+digkYuAGSaQZFHFz2P/cTaLmhF52QeSb45
+Jwxd+uSVGHlqOZpOeJpCFZ5J+rkAkFjQ0N1tah7JJSZUFNsrkeJUJMIBi8jyaEKIhKPomnC91Uo+NB
+yyaJ5umnnpInIFh4t6ZSpGaAVmizqjpByDegYl8tPE0phCYrhcMWSv+uAqHfgH88ak5UXZmlKLVJhd
+dj78s1Fxnzo6yUCrV6rrDOkluG+QzCAUTbCwf9SrmMLzK6p+OPHx7DF+bsfMRq7Ec61Av9i6GLw23r
+idnZ+/OO0a99pbIrJkproCQMA17OPG6suq3cca5ruDfXCCDoS7BEdvmJn5otdqscn+uogRHHXs8cbh
+EIfYaDY1AkrC0cqwcZpnM6ludx72x0p7Fo/hZAcpJDjax0UdHavMKAbiKltMWCF3xxh9k25N/Viud8
+ba78iCvUkt+V6BpwMlErmcgc502x+u1nSxJSJP9Mi52awD1V4yB/QHONsnU3L+A/zR4VL/indx/y64
+gqcj+qgTeweM86f0Qy1QVbvmWH1D9h+alqg254QD8HJXHvjQaGOqEqC22M54PcftZVKVSQG9jhkv7C
+JyTyDoAJfPdu8v7DRZAxsP/ky9MJ3OL36DJfCFPASC3/aXlfLOOON9vGZZHydGf8LnxYJuuVIbl83y
+Az5n/RPz07E+9+zw2A2ahz4HxHo9Kt79HTMx1Q7ma7zAzHgHqYH0SoZWyTuOLMiHwSfZDAQTn0ajk9
+YQqodnUYjByQZhZak9Wu4gYQsMyEpIOAOQKze8CmEF45KuAHTvIDOfHJNipwoHMuGHBnJElUoDmAyX
+c2Qm/R8Ah/iILCCJOEokGowdhDYc/yoL+vpRGwyVSCWFYZNljkhEirGXsalWcAgOdeAdoXcktF2udb
+qbUhjWyMQxYO01o6KYKOr6iK3fE4MaS+DsvBsGOBaMb0Y6IxADaJhFICaOLmiWTlDAnY1KzDG4ambL
+cWBA8mUzjJsN2KjSaSXGqMCVXYpYkj33mcIApyhQf6YqgeNAmNvuC0t4CsDbSshZJkCS1eNisKqlyG
+cF8G2JeiDX6tO6Mv0SmjCa3MFb0bJaGPMU0X7c8XcpvMaOQmCajwSeY9G0WqbBmKv34DsMIEztU6Y2
+KiDlFdt6jnCSqx7Dmt6XnqSKaFFHNO5+FmODxMCWBEaco77lNDGXBM0ECYB/+s7nKFdwSF5hgXumQe
+EZ7amRg39RHy3zIjyRCykQh8Zo2iviRKyTDn/zx6EefptJj2Cw+Ep2FSc01U5ry4KLPYsTyWnVGnvb
+UpyGlhjBUljyjHhWpf8OFaXwhp9O4T1gU9UeyPPa8A2l0p1kNqPXEVRm1AOs1oAGZU596t6SOR2mcB
+Oco1srWtkaVrMUzIErrKri85keKqRQYX9VX0/eAUK1hrSu6HMEX3Qh2sCh0q0D2CtnUqS4hj62sE/z
+aDs2Sg7MBS6xnQeooc2R2tC9YrKpEi9pLXfYXp20tDCpSP8rKlrD4axprb9u1Df5hSbz9QU0cRpfgn
+kiIzwKucd0wsEHlLpe5yHXuc6FrNelOl7pY2+11kTWx7VpRu97dXA3DO1vbkhcb4zyvERYajQgAADs
+='''.decode('base64'), mimetype='image/png')
+
+
+TEMPLATE = u'''\
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<title>WSGI Information</title>
+<style type="text/css">
+ @import url(http://fonts.googleapis.com/css?family=Ubuntu);
+
+ body { font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+ 'Verdana', sans-serif; background-color: white; color: #000;
+ font-size: 15px; text-align: center; }
+ #logo { float: right; padding: 0 0 10px 10px; }
+ div.box { text-align: left; width: 45em; margin: auto; padding: 50px 0;
+ background-color: white; }
+ h1, h2 { font-family: 'Ubuntu', 'Lucida Grande', 'Lucida Sans Unicode',
+ 'Geneva', 'Verdana', sans-serif; font-weight: normal; }
+ h1 { margin: 0 0 30px 0; }
+ h2 { font-size: 1.4em; margin: 1em 0 0.5em 0; }
+ table { width: 100%%; border-collapse: collapse; border: 1px solid #AFC5C9 }
+ table th { background-color: #AFC1C4; color: white; font-size: 0.72em;
+ font-weight: normal; width: 18em; vertical-align: top;
+ padding: 0.5em 0 0.1em 0.5em; }
+ table td { border: 1px solid #AFC5C9; padding: 0.1em 0 0.1em 0.5em; }
+ code { font-family: 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono',
+ monospace; font-size: 0.7em; }
+ ul li { line-height: 1.5em; }
+ ul.path { font-size: 0.7em; margin: 0 -30px; padding: 8px 30px;
+ list-style: none; background: #E8EFF0; }
+ ul.path li { line-height: 1.6em; }
+ li.virtual { color: #999; text-decoration: underline; }
+ li.exp { background: white; }
+</style>
+<div class="box">
+ <img src="?resource=logo" id="logo" alt="[The Werkzeug Logo]" />
+ <h1>WSGI Information</h1>
+ <p>
+ This page displays all available information about the WSGI server and
+ the underlying Python interpreter.
+ <h2 id="python-interpreter">Python Interpreter</h2>
+ <table>
+ <tr>
+ <th>Python Version
+ <td>%(python_version)s
+ <tr>
+ <th>Platform
+ <td>%(platform)s [%(os)s]
+ <tr>
+ <th>API Version
+ <td>%(api_version)s
+ <tr>
+ <th>Byteorder
+ <td>%(byteorder)s
+ <tr>
+ <th>Werkzeug Version
+ <td>%(werkzeug_version)s
+ </table>
+ <h2 id="wsgi-environment">WSGI Environment</h2>
+ <table>%(wsgi_env)s</table>
+ <h2 id="installed-eggs">Installed Eggs</h2>
+ <p>
+ The following python packages were installed on the system as
+ Python eggs:
+ <ul>%(python_eggs)s</ul>
+ <h2 id="sys-path">System Path</h2>
+ <p>
+ The following paths are the current contents of the load path. The
+ following entries are looked up for Python packages. Note that not
+ all items in this path are folders. Gray and underlined items are
+ entries pointing to invalid resources or used by custom import hooks
+ such as the zip importer.
+ <p>
+ Items with a bright background were expanded for display from a relative
+ path. If you encounter such paths in the output you might want to check
+ your setup as relative paths are usually problematic in multithreaded
+ environments.
+ <ul class="path">%(sys_path)s</ul>
+</div>
+'''
+
+
+def iter_sys_path():
+ if os.name == 'posix':
+ def strip(x):
+ prefix = os.path.expanduser('~')
+ if x.startswith(prefix):
+ x = '~' + x[len(prefix):]
+ return x
+ else:
+ strip = lambda x: x
+
+ cwd = os.path.abspath(os.getcwd())
+ for item in sys.path:
+ path = os.path.join(cwd, item or os.path.curdir)
+ yield strip(os.path.normpath(path)), \
+ not os.path.isdir(path), path != item
+
+
+def render_testapp(req):
+ try:
+ import pkg_resources
+ except ImportError:
+ eggs = ()
+ else:
+ eggs = list(pkg_resources.working_set)
+ eggs.sort(lambda a, b: cmp(a.project_name.lower(),
+ b.project_name.lower()))
+ python_eggs = []
+ for egg in eggs:
+ try:
+ version = egg.version
+ except (ValueError, AttributeError):
+ version = 'unknown'
+ python_eggs.append('<li>%s <small>[%s]</small>' % (
+ escape(egg.project_name),
+ escape(version)
+ ))
+
+ wsgi_env = []
+ sorted_environ = req.environ.items()
+ sorted_environ.sort(key=lambda x: repr(x[0]).lower())
+ for key, value in sorted_environ:
+ wsgi_env.append('<tr><th>%s<td><code>%s</code>' % (
+ escape(str(key)),
+ ' '.join(wrap(escape(repr(value))))
+ ))
+
+ sys_path = []
+ for item, virtual, expanded in iter_sys_path():
+ class_ = []
+ if virtual:
+ class_.append('virtual')
+ if expanded:
+ class_.append('exp')
+ sys_path.append('<li%s>%s' % (
+ class_ and ' class="%s"' % ' '.join(class_) or '',
+ escape(item)
+ ))
+
+ return TEMPLATE % {
+ 'python_version': '<br>'.join(escape(sys.version).splitlines()),
+ 'platform': escape(sys.platform),
+ 'os': escape(os.name),
+ 'api_version': sys.api_version,
+ 'byteorder': sys.byteorder,
+ 'werkzeug_version': werkzeug.__version__,
+ 'python_eggs': '\n'.join(python_eggs),
+ 'wsgi_env': '\n'.join(wsgi_env),
+ 'sys_path': '\n'.join(sys_path)
+ }
+
+
+def test_app(environ, start_response):
+ """Simple test application that dumps the environment. You can use
+ it to check if Werkzeug is working properly:
+
+ .. sourcecode:: pycon
+
+ >>> from werkzeug.serving import run_simple
+ >>> from werkzeug.testapp import test_app
+ >>> run_simple('localhost', 3000, test_app)
+ * Running on http://localhost:3000/
+
+ The application displays important information from the WSGI environment,
+ the Python interpreter and the installed libraries.
+ """
+ req = Request(environ, populate_request=False)
+ if req.args.get('resource') == 'logo':
+ response = logo
+ else:
+ response = Response(render_testapp(req), mimetype='text/html')
+ return response(environ, start_response)
+
+
+if __name__ == '__main__':
+ from werkzeug.serving import run_simple
+ run_simple('localhost', 5000, test_app, use_reloader=True)
diff --git a/websdk/werkzeug/testsuite/__init__.py b/websdk/werkzeug/testsuite/__init__.py
new file mode 100644
index 0000000..e1c8acd
--- /dev/null
+++ b/websdk/werkzeug/testsuite/__init__.py
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite
+ ~~~~~~~~~~~~~~~~~~
+
+ Contains all test Werkzeug tests.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import unittest
+from werkzeug.utils import import_string, find_modules
+
+
+def iter_suites(package):
+ """Yields all testsuites."""
+ for module in find_modules(package, include_packages=True):
+ mod = import_string(module)
+ if hasattr(mod, 'suite'):
+ yield mod.suite()
+
+
+def find_all_tests(suite):
+ """Yields all the tests and their names from a given suite."""
+ suites = [suite]
+ while suites:
+ s = suites.pop()
+ try:
+ suites.extend(s)
+ except TypeError:
+ yield s, '%s.%s.%s' % (
+ s.__class__.__module__,
+ s.__class__.__name__,
+ s._testMethodName
+ )
+
+
+class WerkzeugTestCase(unittest.TestCase):
+ """Baseclass for all the tests that Werkzeug uses. Use these
+ methods for testing instead of the camelcased ones in the
+ baseclass for consistency.
+ """
+
+ def setup(self):
+ pass
+
+ def teardown(self):
+ pass
+
+ def setUp(self):
+ self.setup()
+
+ def tearDown(self):
+ unittest.TestCase.tearDown(self)
+ self.teardown()
+
+ def assert_equal(self, x, y):
+ return self.assertEqual(x, y)
+
+ def assert_not_equal(self, x, y):
+ return self.assertNotEqual(x, y)
+
+ def assert_raises(self, exc_type, callable=None, *args, **kwargs):
+ catcher = _ExceptionCatcher(self, exc_type)
+ if callable is None:
+ return catcher
+ with catcher:
+ callable(*args, **kwargs)
+
+
+class _ExceptionCatcher(object):
+
+ def __init__(self, test_case, exc_type):
+ self.test_case = test_case
+ self.exc_type = exc_type
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ exception_name = self.exc_type.__name__
+ if exc_type is None:
+ self.test_case.fail('Expected exception of type %r' %
+ exception_name)
+ elif not issubclass(exc_type, self.exc_type):
+ raise exc_type, exc_value, tb
+ return True
+
+
+class BetterLoader(unittest.TestLoader):
+ """A nicer loader that solves two problems. First of all we are setting
+ up tests from different sources and we're doing this programmatically
+ which breaks the default loading logic so this is required anyways.
+ Secondly this loader has a nicer interpolation for test names than the
+ default one so you can just do ``run-tests.py ViewTestCase`` and it
+ will work.
+ """
+
+ def getRootSuite(self):
+ return suite()
+
+ def loadTestsFromName(self, name, module=None):
+ root = self.getRootSuite()
+ if name == 'suite':
+ return root
+
+ all_tests = []
+ for testcase, testname in find_all_tests(root):
+ if testname == name or \
+ testname.endswith('.' + name) or \
+ ('.' + name + '.') in testname or \
+ testname.startswith(name + '.'):
+ all_tests.append(testcase)
+
+ if not all_tests:
+ raise LookupError('could not find test case for "%s"' % name)
+
+ if len(all_tests) == 1:
+ return all_tests[0]
+ rv = unittest.TestSuite()
+ for test in all_tests:
+ rv.addTest(test)
+ return rv
+
+
+def suite():
+ """A testsuite that has all the Flask tests. You can use this
+ function to integrate the Flask tests into your own testsuite
+ in case you want to test that monkeypatches to Flask do not
+ break it.
+ """
+ suite = unittest.TestSuite()
+ for other_suite in iter_suites(__name__):
+ suite.addTest(other_suite)
+ return suite
+
+
+def main():
+ """Runs the testsuite as command line application."""
+ try:
+ unittest.main(testLoader=BetterLoader(), defaultTest='suite')
+ except Exception, e:
+ print 'Error: %s' % e
diff --git a/websdk/werkzeug/testsuite/compat.py b/websdk/werkzeug/testsuite/compat.py
new file mode 100644
index 0000000..f5487c3
--- /dev/null
+++ b/websdk/werkzeug/testsuite/compat.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.compat
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Ensure that old stuff does not break on update.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+import warnings
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug.wrappers import Response
+from werkzeug.test import create_environ
+
+
+class CompatTestCase(WerkzeugTestCase):
+
+ def test_old_imports(self):
+ from werkzeug.utils import Headers, MultiDict, CombinedMultiDict, \
+ Headers, EnvironHeaders
+ from werkzeug.http import Accept, MIMEAccept, CharsetAccept, \
+ LanguageAccept, ETags, HeaderSet, WWWAuthenticate, \
+ Authorization
+
+ def test_exposed_werkzeug_mod(self):
+ import werkzeug
+ for key in werkzeug.__all__:
+ # deprecated, skip it
+ if key in ('templates', 'Template'):
+ continue
+ getattr(werkzeug, key)
+
+ def test_fix_headers_in_response(self):
+ # ignore some warnings werkzeug emits for backwards compat
+ for msg in ['called into deprecated fix_headers',
+ 'fix_headers changed behavior']:
+ warnings.filterwarnings('ignore', message=msg,
+ category=DeprecationWarning)
+
+ class MyResponse(Response):
+ def fix_headers(self, environ):
+ Response.fix_headers(self, environ)
+ self.headers['x-foo'] = "meh"
+ myresp = MyResponse('Foo')
+ resp = Response.from_app(myresp, create_environ(method='GET'))
+ assert resp.headers['x-foo'] == 'meh'
+ assert resp.data == 'Foo'
+
+ warnings.resetwarnings()
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(CompatTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/contrib/__init__.py b/websdk/werkzeug/testsuite/contrib/__init__.py
new file mode 100644
index 0000000..b36a4ef
--- /dev/null
+++ b/websdk/werkzeug/testsuite/contrib/__init__.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.contrib
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the contrib modules.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+from werkzeug.testsuite import iter_suites
+
+
+def suite():
+ suite = unittest.TestSuite()
+ for other_suite in iter_suites(__name__):
+ suite.addTest(other_suite)
+ return suite
diff --git a/websdk/werkzeug/testsuite/contrib/cache.py b/websdk/werkzeug/testsuite/contrib/cache.py
new file mode 100644
index 0000000..209189a
--- /dev/null
+++ b/websdk/werkzeug/testsuite/contrib/cache.py
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.cache
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the cache system
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import time
+import unittest
+import tempfile
+import shutil
+
+from werkzeug.testsuite import WerkzeugTestCase
+from werkzeug.contrib import cache
+
+try:
+ import redis
+except ImportError:
+ redis = None
+
+
+
+class SimpleCacheTestCase(WerkzeugTestCase):
+
+ def test_get_dict(self):
+ c = cache.SimpleCache()
+ c.set('a', 'a')
+ c.set('b', 'b')
+ d = c.get_dict('a', 'b')
+ assert 'a' in d
+ assert 'a' == d['a']
+ assert 'b' in d
+ assert 'b' == d['b']
+
+ def test_set_many(self):
+ c = cache.SimpleCache()
+ c.set_many({0: 0, 1: 1, 2: 4})
+ assert c.get(2) == 4
+ c.set_many((i, i*i) for i in xrange(3))
+ assert c.get(2) == 4
+
+
+class FileSystemCacheTestCase(WerkzeugTestCase):
+
+ def test_set_get(self):
+ tmp_dir = tempfile.mkdtemp()
+ try:
+ c = cache.FileSystemCache(cache_dir=tmp_dir)
+ for i in range(3):
+ c.set(str(i), i * i)
+ for i in range(3):
+ result = c.get(str(i))
+ assert result == i * i
+ finally:
+ shutil.rmtree(tmp_dir)
+
+ def test_filesystemcache_prune(self):
+ THRESHOLD = 13
+ tmp_dir = tempfile.mkdtemp()
+ c = cache.FileSystemCache(cache_dir=tmp_dir, threshold=THRESHOLD)
+ for i in range(2 * THRESHOLD):
+ c.set(str(i), i)
+ cache_files = os.listdir(tmp_dir)
+ shutil.rmtree(tmp_dir)
+ assert len(cache_files) <= THRESHOLD
+
+
+ def test_filesystemcache_clear(self):
+ tmp_dir = tempfile.mkdtemp()
+ c = cache.FileSystemCache(cache_dir=tmp_dir)
+ c.set('foo', 'bar')
+ cache_files = os.listdir(tmp_dir)
+ assert len(cache_files) == 1
+ c.clear()
+ cache_files = os.listdir(tmp_dir)
+ assert len(cache_files) == 0
+ shutil.rmtree(tmp_dir)
+
+
+class RedisCacheTestCase(WerkzeugTestCase):
+
+ def make_cache(self):
+ return cache.RedisCache(key_prefix='werkzeug-test-case:')
+
+ def teardown(self):
+ self.make_cache().clear()
+
+ def test_get_set(self):
+ c = self.make_cache()
+ c.set('foo', 'bar')
+ assert c.get('foo') == 'bar'
+
+ def test_get_many(self):
+ c = self.make_cache()
+ c.set('foo', 'bar')
+ c.set('spam', 'eggs')
+ assert c.get_many('foo', 'spam') == ['bar', 'eggs']
+
+ def test_set_many(self):
+ c = self.make_cache()
+ c.set_many({'foo': 'bar', 'spam': 'eggs'})
+ assert c.get('foo') == 'bar'
+ assert c.get('spam') == 'eggs'
+
+ def test_expire(self):
+ c = self.make_cache()
+ c.set('foo', 'bar', 1)
+ time.sleep(2)
+ assert c.get('foo') is None
+
+ def test_add(self):
+ c = self.make_cache()
+ # sanity check that add() works like set()
+ c.add('foo', 'bar')
+ assert c.get('foo') == 'bar'
+ c.add('foo', 'qux')
+ assert c.get('foo') == 'bar'
+
+ def test_delete(self):
+ c = self.make_cache()
+ c.add('foo', 'bar')
+ assert c.get('foo') == 'bar'
+ c.delete('foo')
+ assert c.get('foo') is None
+
+ def test_delete_many(self):
+ c = self.make_cache()
+ c.add('foo', 'bar')
+ c.add('spam', 'eggs')
+ c.delete_many('foo', 'spam')
+ assert c.get('foo') is None
+ assert c.get('spam') is None
+
+ def test_inc_dec(self):
+ c = self.make_cache()
+ c.set('foo', 1)
+ assert c.inc('foo') == 2
+ assert c.dec('foo') == 1
+ c.delete('foo')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(SimpleCacheTestCase))
+ suite.addTest(unittest.makeSuite(FileSystemCacheTestCase))
+ if redis is not None:
+ suite.addTest(unittest.makeSuite(RedisCacheTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/contrib/fixers.py b/websdk/werkzeug/testsuite/contrib/fixers.py
new file mode 100644
index 0000000..0422f8b
--- /dev/null
+++ b/websdk/werkzeug/testsuite/contrib/fixers.py
@@ -0,0 +1,187 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.fixers
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Server / Browser fixers.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from werkzeug.testsuite import WerkzeugTestCase
+from werkzeug.datastructures import ResponseCacheControl
+from werkzeug.http import parse_cache_control_header
+
+from werkzeug.test import create_environ, Client
+from werkzeug.wrappers import Request, Response
+from werkzeug.contrib import fixers
+from werkzeug.utils import redirect
+
+
+@Request.application
+def path_check_app(request):
+ return Response('PATH_INFO: %s\nSCRIPT_NAME: %s' % (
+ request.environ.get('PATH_INFO', ''),
+ request.environ.get('SCRIPT_NAME', '')
+ ))
+
+
+class ServerFixerTestCase(WerkzeugTestCase):
+
+ def test_lighttpd_cgi_root_fix(self):
+ app = fixers.LighttpdCGIRootFix(path_check_app)
+ response = Response.from_app(app, dict(create_environ(),
+ SCRIPT_NAME='/foo',
+ PATH_INFO='/bar',
+ SERVER_SOFTWARE='lighttpd/1.4.27'
+ ))
+ assert response.data == 'PATH_INFO: /foo/bar\nSCRIPT_NAME: '
+
+ def test_path_info_from_request_uri_fix(self):
+ app = fixers.PathInfoFromRequestUriFix(path_check_app)
+ for key in 'REQUEST_URI', 'REQUEST_URL', 'UNENCODED_URL':
+ env = dict(create_environ(), SCRIPT_NAME='/test', PATH_INFO='/?????')
+ env[key] = '/test/foo%25bar?drop=this'
+ response = Response.from_app(app, env)
+ assert response.data == 'PATH_INFO: /foo%bar\nSCRIPT_NAME: /test'
+
+ def test_proxy_fix(self):
+ """Test the ProxyFix fixer"""
+ @fixers.ProxyFix
+ @Request.application
+ def app(request):
+ return Response('%s|%s' % (
+ request.remote_addr,
+ # do not use request.host as this fixes too :)
+ request.environ['HTTP_HOST']
+ ))
+ environ = dict(create_environ(),
+ HTTP_X_FORWARDED_PROTO="https",
+ HTTP_X_FORWARDED_HOST='example.com',
+ HTTP_X_FORWARDED_FOR='1.2.3.4, 5.6.7.8',
+ REMOTE_ADDR='127.0.0.1',
+ HTTP_HOST='fake'
+ )
+
+ response = Response.from_app(app, environ)
+
+ assert response.data == '1.2.3.4|example.com'
+
+ # And we must check that if it is a redirection it is
+ # correctly done:
+
+ redirect_app = redirect('/foo/bar.hml')
+ response = Response.from_app(redirect_app, environ)
+
+ wsgi_headers = response.get_wsgi_headers(environ)
+ assert wsgi_headers['Location'] == 'https://example.com/foo/bar.hml'
+
+ def test_proxy_fix_weird_enum(self):
+ @fixers.ProxyFix
+ @Request.application
+ def app(request):
+ return Response(request.remote_addr)
+ environ = dict(create_environ(),
+ HTTP_X_FORWARDED_FOR=',',
+ REMOTE_ADDR='127.0.0.1',
+ )
+
+ response = Response.from_app(app, environ)
+ self.assert_equal(response.data, '127.0.0.1')
+
+ def test_header_rewriter_fix(self):
+ """Test the HeaderRewriterFix fixer"""
+ @Request.application
+ def application(request):
+ return Response("", headers=[
+ ('X-Foo', 'bar')
+ ])
+ application = fixers.HeaderRewriterFix(application, ('X-Foo',), (('X-Bar', '42'),))
+ response = Response.from_app(application, create_environ())
+ assert response.headers['Content-Type'] == 'text/plain; charset=utf-8'
+ assert 'X-Foo' not in response.headers
+ assert response.headers['X-Bar'] == '42'
+
+
+class BrowserFixerTestCase(WerkzeugTestCase):
+
+ def test_ie_fixes(self):
+ @fixers.InternetExplorerFix
+ @Request.application
+ def application(request):
+ response = Response('binary data here', mimetype='application/vnd.ms-excel')
+ response.headers['Vary'] = 'Cookie'
+ response.headers['Content-Disposition'] = 'attachment; filename=foo.xls'
+ return response
+
+ c = Client(application, Response)
+ response = c.get('/', headers=[
+ ('User-Agent', 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)')
+ ])
+
+ # IE gets no vary
+ assert response.data == 'binary data here'
+ assert 'vary' not in response.headers
+ assert response.headers['content-disposition'] == 'attachment; filename=foo.xls'
+ assert response.headers['content-type'] == 'application/vnd.ms-excel'
+
+ # other browsers do
+ c = Client(application, Response)
+ response = c.get('/')
+ assert response.data == 'binary data here'
+ assert 'vary' in response.headers
+
+ cc = ResponseCacheControl()
+ cc.no_cache = True
+
+ @fixers.InternetExplorerFix
+ @Request.application
+ def application(request):
+ response = Response('binary data here', mimetype='application/vnd.ms-excel')
+ response.headers['Pragma'] = ', '.join(pragma)
+ response.headers['Cache-Control'] = cc.to_header()
+ response.headers['Content-Disposition'] = 'attachment; filename=foo.xls'
+ return response
+
+
+ # IE has no pragma or cache control
+ pragma = ('no-cache',)
+ c = Client(application, Response)
+ response = c.get('/', headers=[
+ ('User-Agent', 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)')
+ ])
+ assert response.data == 'binary data here'
+ assert 'pragma' not in response.headers
+ assert 'cache-control' not in response.headers
+ assert response.headers['content-disposition'] == 'attachment; filename=foo.xls'
+
+ # IE has simplified pragma
+ pragma = ('no-cache', 'x-foo')
+ cc.proxy_revalidate = True
+ response = c.get('/', headers=[
+ ('User-Agent', 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)')
+ ])
+ assert response.data == 'binary data here'
+ assert response.headers['pragma'] == 'x-foo'
+ assert response.headers['cache-control'] == 'proxy-revalidate'
+ assert response.headers['content-disposition'] == 'attachment; filename=foo.xls'
+
+ # regular browsers get everything
+ response = c.get('/')
+ assert response.data == 'binary data here'
+ assert response.headers['pragma'] == 'no-cache, x-foo'
+ cc = parse_cache_control_header(response.headers['cache-control'],
+ cls=ResponseCacheControl)
+ assert cc.no_cache
+ assert cc.proxy_revalidate
+ assert response.headers['content-disposition'] == 'attachment; filename=foo.xls'
+
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ServerFixerTestCase))
+ suite.addTest(unittest.makeSuite(BrowserFixerTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/contrib/iterio.py b/websdk/werkzeug/testsuite/contrib/iterio.py
new file mode 100644
index 0000000..cddbd6e
--- /dev/null
+++ b/websdk/werkzeug/testsuite/contrib/iterio.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.iterio
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the iterio object.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from werkzeug.testsuite import WerkzeugTestCase
+from werkzeug.contrib.iterio import IterIO, greenlet
+
+
+class IterOTestSuite(WerkzeugTestCase):
+
+ def test_basic(self):
+ io = IterIO(["Hello", "World", "1", "2", "3"])
+ assert io.tell() == 0
+ assert io.read(2) == "He"
+ assert io.tell() == 2
+ assert io.read(3) == "llo"
+ assert io.tell() == 5
+ io.seek(0)
+ assert io.read(5) == "Hello"
+ assert io.tell() == 5
+ assert io._buf == "Hello"
+ assert io.read() == "World123"
+ assert io.tell() == 13
+ io.close()
+ assert io.closed
+
+ io = IterIO(["Hello\n", "World!"])
+ assert io.readline() == 'Hello\n'
+ assert io._buf == 'Hello\n'
+ assert io.read() == 'World!'
+ assert io._buf == 'Hello\nWorld!'
+ assert io.tell() == 12
+ io.seek(0)
+ assert io.readlines() == ['Hello\n', 'World!']
+
+ io = IterIO(["foo\n", "bar"])
+ io.seek(-4, 2)
+ assert io.read(4) == '\nbar'
+
+ self.assert_raises(IOError, io.seek, 2, 100)
+ io.close()
+ self.assert_raises(ValueError, io.read)
+
+
+class IterITestSuite(WerkzeugTestCase):
+
+ def test_basic(self):
+ def producer(out):
+ out.write('1\n')
+ out.write('2\n')
+ out.flush()
+ out.write('3\n')
+ iterable = IterIO(producer)
+ self.assert_equal(iterable.next(), '1\n2\n')
+ self.assert_equal(iterable.next(), '3\n')
+ self.assert_raises(StopIteration, iterable.next)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(IterOTestSuite))
+ if greenlet is not None:
+ suite.addTest(unittest.makeSuite(IterITestSuite))
+ return suite
diff --git a/websdk/werkzeug/testsuite/contrib/securecookie.py b/websdk/werkzeug/testsuite/contrib/securecookie.py
new file mode 100644
index 0000000..fb10fa1
--- /dev/null
+++ b/websdk/werkzeug/testsuite/contrib/securecookie.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.securecookie
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the secure cookie.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug.utils import parse_cookie
+from werkzeug.wrappers import Request, Response
+from werkzeug.contrib.securecookie import SecureCookie
+
+
+class SecureCookieTestCase(WerkzeugTestCase):
+
+ def test_basic_support(self):
+ c = SecureCookie(secret_key='foo')
+ assert c.new
+ print c.modified, c.should_save
+ assert not c.modified
+ assert not c.should_save
+ c['x'] = 42
+ assert c.modified
+ assert c.should_save
+ s = c.serialize()
+
+ c2 = SecureCookie.unserialize(s, 'foo')
+ assert c is not c2
+ assert not c2.new
+ assert not c2.modified
+ assert not c2.should_save
+ assert c2 == c
+
+ c3 = SecureCookie.unserialize(s, 'wrong foo')
+ assert not c3.modified
+ assert not c3.new
+ assert c3 == {}
+
+ def test_wrapper_support(self):
+ req = Request.from_values()
+ resp = Response()
+ c = SecureCookie.load_cookie(req, secret_key='foo')
+ assert c.new
+ c['foo'] = 42
+ assert c.secret_key == 'foo'
+ c.save_cookie(resp)
+
+ req = Request.from_values(headers={
+ 'Cookie': 'session="%s"' % parse_cookie(resp.headers['set-cookie'])['session']
+ })
+ c2 = SecureCookie.load_cookie(req, secret_key='foo')
+ assert not c2.new
+ assert c2 == c
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(SecureCookieTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/contrib/sessions.py b/websdk/werkzeug/testsuite/contrib/sessions.py
new file mode 100644
index 0000000..18015a6
--- /dev/null
+++ b/websdk/werkzeug/testsuite/contrib/sessions.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.sessions
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Added tests for the sessions.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+import shutil
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug.contrib.sessions import FilesystemSessionStore
+
+from tempfile import mkdtemp, gettempdir
+
+
+class SessionTestCase(WerkzeugTestCase):
+
+ def setup(self):
+ self.session_folder = mkdtemp()
+
+ def teardown(self):
+ shutil.rmtree(self.session_folder)
+
+ def test_default_tempdir(self):
+ store = FilesystemSessionStore()
+ assert store.path == gettempdir()
+
+ def test_basic_fs_sessions(self):
+ store = FilesystemSessionStore(self.session_folder)
+ x = store.new()
+ assert x.new
+ assert not x.modified
+ x['foo'] = [1, 2, 3]
+ assert x.modified
+ store.save(x)
+
+ x2 = store.get(x.sid)
+ assert not x2.new
+ assert not x2.modified
+ assert x2 is not x
+ assert x2 == x
+ x2['test'] = 3
+ assert x2.modified
+ assert not x2.new
+ store.save(x2)
+
+ x = store.get(x.sid)
+ store.delete(x)
+ x2 = store.get(x.sid)
+ # the session is not new when it was used previously.
+ assert not x2.new
+
+ def test_renewing_fs_session(self):
+ store = FilesystemSessionStore(self.session_folder, renew_missing=True)
+ x = store.new()
+ store.save(x)
+ store.delete(x)
+ x2 = store.get(x.sid)
+ assert x2.new
+
+ def test_fs_session_lising(self):
+ store = FilesystemSessionStore(self.session_folder, renew_missing=True)
+ sessions = set()
+ for x in xrange(10):
+ sess = store.new()
+ store.save(sess)
+ sessions.add(sess.sid)
+
+ listed_sessions = set(store.list())
+ assert sessions == listed_sessions
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(SessionTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/contrib/wrappers.py b/websdk/werkzeug/testsuite/contrib/wrappers.py
new file mode 100644
index 0000000..e22d9a4
--- /dev/null
+++ b/websdk/werkzeug/testsuite/contrib/wrappers.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.contrib.wrappers
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Added tests for the sessions.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import unittest
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug.contrib import wrappers
+from werkzeug import routing
+from werkzeug.wrappers import Request, Response
+
+
+class WrappersTestCase(WerkzeugTestCase):
+
+ def test_reverse_slash_behavior(self):
+ class MyRequest(wrappers.ReverseSlashBehaviorRequestMixin, Request):
+ pass
+ req = MyRequest.from_values('/foo/bar', 'http://example.com/test')
+ assert req.url == 'http://example.com/test/foo/bar'
+ assert req.path == 'foo/bar'
+ assert req.script_root == '/test/'
+
+ # make sure the routing system works with the slashes in
+ # reverse order as well.
+ map = routing.Map([routing.Rule('/foo/bar', endpoint='foo')])
+ adapter = map.bind_to_environ(req.environ)
+ assert adapter.match() == ('foo', {})
+ adapter = map.bind(req.host, req.script_root)
+ assert adapter.match(req.path) == ('foo', {})
+
+ def test_dynamic_charset_request_mixin(self):
+ class MyRequest(wrappers.DynamicCharsetRequestMixin, Request):
+ pass
+ env = {'CONTENT_TYPE': 'text/html'}
+ req = MyRequest(env)
+ assert req.charset == 'latin1'
+
+ env = {'CONTENT_TYPE': 'text/html; charset=utf-8'}
+ req = MyRequest(env)
+ assert req.charset == 'utf-8'
+
+ env = {'CONTENT_TYPE': 'application/octet-stream'}
+ req = MyRequest(env)
+ assert req.charset == 'latin1'
+ assert req.url_charset == 'latin1'
+
+ MyRequest.url_charset = 'utf-8'
+ env = {'CONTENT_TYPE': 'application/octet-stream'}
+ req = MyRequest(env)
+ assert req.charset == 'latin1'
+ assert req.url_charset == 'utf-8'
+
+ def return_ascii(x):
+ return "ascii"
+ env = {'CONTENT_TYPE': 'text/plain; charset=x-weird-charset'}
+ req = MyRequest(env)
+ req.unknown_charset = return_ascii
+ assert req.charset == 'ascii'
+ assert req.url_charset == 'utf-8'
+
+ def test_dynamic_charset_response_mixin(self):
+ class MyResponse(wrappers.DynamicCharsetResponseMixin, Response):
+ default_charset = 'utf-7'
+ resp = MyResponse(mimetype='text/html')
+ assert resp.charset == 'utf-7'
+ resp.charset = 'utf-8'
+ assert resp.charset == 'utf-8'
+ assert resp.mimetype == 'text/html'
+ assert resp.mimetype_params == {'charset': 'utf-8'}
+ resp.mimetype_params['charset'] = 'iso-8859-15'
+ assert resp.charset == 'iso-8859-15'
+ resp.data = u'Hällo Wörld'
+ assert ''.join(resp.iter_encoded()) == \
+ u'Hällo Wörld'.encode('iso-8859-15')
+ del resp.headers['content-type']
+ try:
+ resp.charset = 'utf-8'
+ except TypeError, e:
+ pass
+ else:
+ assert False, 'expected type error on charset setting without ct'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(WrappersTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/datastructures.py b/websdk/werkzeug/testsuite/datastructures.py
new file mode 100644
index 0000000..3efedb7
--- /dev/null
+++ b/websdk/werkzeug/testsuite/datastructures.py
@@ -0,0 +1,618 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.datastructures
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the functionality of the provided Werkzeug
+ datastructures.
+
+ TODO:
+
+ - FileMultiDict
+ - convert to proper asserts
+ - Immutable types undertested
+ - Split up dict tests
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import unittest
+import pickle
+from copy import copy
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug import datastructures
+from werkzeug.exceptions import BadRequestKeyError
+
+
+class MutableMultiDictBaseTestCase(WerkzeugTestCase):
+ storage_class = None
+
+ def test_pickle(self):
+ cls = self.storage_class
+
+ for protocol in xrange(pickle.HIGHEST_PROTOCOL + 1):
+ d = cls()
+ d.setlist('foo', [1, 2, 3, 4])
+ d.setlist('bar', 'foo bar baz'.split())
+ s = pickle.dumps(d, protocol)
+ ud = pickle.loads(s)
+ self.assert_equal(type(ud), type(d))
+ self.assert_equal(ud, d)
+ self.assert_equal(pickle.loads(
+ s.replace('werkzeug.datastructures', 'werkzeug')), d)
+ ud['newkey'] = 'bla'
+ self.assert_not_equal(ud, d)
+
+ def test_basic_interface(self):
+ md = self.storage_class()
+ assert isinstance(md, dict)
+
+ mapping = [('a', 1), ('b', 2), ('a', 2), ('d', 3),
+ ('a', 1), ('a', 3), ('d', 4), ('c', 3)]
+ md = self.storage_class(mapping)
+
+ # simple getitem gives the first value
+ assert md['a'] == 1
+ assert md['c'] == 3
+ with self.assert_raises(KeyError):
+ md['e']
+ assert md.get('a') == 1
+
+ # list getitem
+ assert md.getlist('a') == [1, 2, 1, 3]
+ assert md.getlist('d') == [3, 4]
+ # do not raise if key not found
+ assert md.getlist('x') == []
+
+ # simple setitem overwrites all values
+ md['a'] = 42
+ assert md.getlist('a') == [42]
+
+ # list setitem
+ md.setlist('a', [1, 2, 3])
+ assert md['a'] == 1
+ assert md.getlist('a') == [1, 2, 3]
+
+ # verify that it does not change original lists
+ l1 = [1, 2, 3]
+ md.setlist('a', l1)
+ del l1[:]
+ assert md['a'] == 1
+
+ # setdefault, setlistdefault
+ assert md.setdefault('u', 23) == 23
+ assert md.getlist('u') == [23]
+ del md['u']
+
+ md.setlist('u', [-1, -2])
+
+ # delitem
+ del md['u']
+ with self.assert_raises(KeyError):
+ md['u']
+ del md['d']
+ assert md.getlist('d') == []
+
+ # keys, values, items, lists
+ assert list(sorted(md.keys())) == ['a', 'b', 'c']
+ assert list(sorted(md.iterkeys())) == ['a', 'b', 'c']
+
+ assert list(sorted(md.values())) == [1, 2, 3]
+ assert list(sorted(md.itervalues())) == [1, 2, 3]
+
+ assert list(sorted(md.items())) == [('a', 1), ('b', 2), ('c', 3)]
+ assert list(sorted(md.items(multi=True))) == \
+ [('a', 1), ('a', 2), ('a', 3), ('b', 2), ('c', 3)]
+ assert list(sorted(md.iteritems())) == [('a', 1), ('b', 2), ('c', 3)]
+ assert list(sorted(md.iteritems(multi=True))) == \
+ [('a', 1), ('a', 2), ('a', 3), ('b', 2), ('c', 3)]
+
+ assert list(sorted(md.lists())) == [('a', [1, 2, 3]), ('b', [2]), ('c', [3])]
+ assert list(sorted(md.iterlists())) == [('a', [1, 2, 3]), ('b', [2]), ('c', [3])]
+
+ # copy method
+ c = md.copy()
+ assert c['a'] == 1
+ assert c.getlist('a') == [1, 2, 3]
+
+ # copy method 2
+ c = copy(md)
+ assert c['a'] == 1
+ assert c.getlist('a') == [1, 2, 3]
+
+ # update with a multidict
+ od = self.storage_class([('a', 4), ('a', 5), ('y', 0)])
+ md.update(od)
+ assert md.getlist('a') == [1, 2, 3, 4, 5]
+ assert md.getlist('y') == [0]
+
+ # update with a regular dict
+ md = c
+ od = {'a': 4, 'y': 0}
+ md.update(od)
+ assert md.getlist('a') == [1, 2, 3, 4]
+ assert md.getlist('y') == [0]
+
+ # pop, poplist, popitem, popitemlist
+ assert md.pop('y') == 0
+ assert 'y' not in md
+ assert md.poplist('a') == [1, 2, 3, 4]
+ assert 'a' not in md
+ assert md.poplist('missing') == []
+
+ # remaining: b=2, c=3
+ popped = md.popitem()
+ assert popped in [('b', 2), ('c', 3)]
+ popped = md.popitemlist()
+ assert popped in [('b', [2]), ('c', [3])]
+
+ # type conversion
+ md = self.storage_class({'a': '4', 'b': ['2', '3']})
+ assert md.get('a', type=int) == 4
+ assert md.getlist('b', type=int) == [2, 3]
+
+ # repr
+ md = self.storage_class([('a', 1), ('a', 2), ('b', 3)])
+ assert "('a', 1)" in repr(md)
+ assert "('a', 2)" in repr(md)
+ assert "('b', 3)" in repr(md)
+
+ # add and getlist
+ md.add('c', '42')
+ md.add('c', '23')
+ assert md.getlist('c') == ['42', '23']
+ md.add('c', 'blah')
+ assert md.getlist('c', type=int) == [42, 23]
+
+ # setdefault
+ md = self.storage_class()
+ md.setdefault('x', []).append(42)
+ md.setdefault('x', []).append(23)
+ assert md['x'] == [42, 23]
+
+ # to dict
+ md = self.storage_class()
+ md['foo'] = 42
+ md.add('bar', 1)
+ md.add('bar', 2)
+ assert md.to_dict() == {'foo': 42, 'bar': 1}
+ assert md.to_dict(flat=False) == {'foo': [42], 'bar': [1, 2]}
+
+ # popitem from empty dict
+ with self.assert_raises(KeyError):
+ self.storage_class().popitem()
+
+ with self.assert_raises(KeyError):
+ self.storage_class().popitemlist()
+
+ # key errors are of a special type
+ with self.assert_raises(BadRequestKeyError):
+ self.storage_class()[42]
+
+ # setlist works
+ md = self.storage_class()
+ md['foo'] = 42
+ md.setlist('foo', [1, 2])
+ assert md.getlist('foo') == [1, 2]
+
+
+class ImmutableDictBaseTestCase(WerkzeugTestCase):
+ storage_class = None
+
+ def test_follows_dict_interface(self):
+ cls = self.storage_class
+
+ data = {'foo': 1, 'bar': 2, 'baz': 3}
+ d = cls(data)
+
+ self.assert_equal(d['foo'], 1)
+ self.assert_equal(d['bar'], 2)
+ self.assert_equal(d['baz'], 3)
+ self.assert_equal(sorted(d.keys()), ['bar', 'baz', 'foo'])
+ self.assert_('foo' in d)
+ self.assert_('foox' not in d)
+ self.assert_equal(len(d), 3)
+
+ def test_copies_are_mutable(self):
+ cls = self.storage_class
+ immutable = cls({'a': 1})
+ with self.assert_raises(TypeError):
+ immutable.pop('a')
+
+ mutable = immutable.copy()
+ mutable.pop('a')
+ self.assert_('a' in immutable)
+ self.assert_(mutable is not immutable)
+ self.assert_(copy(immutable) is immutable)
+
+ def test_dict_is_hashable(self):
+ cls = self.storage_class
+ immutable = cls({'a': 1, 'b': 2})
+ immutable2 = cls({'a': 2, 'b': 2})
+ x = set([immutable])
+ self.assert_(immutable in x)
+ self.assert_(immutable2 not in x)
+ x.discard(immutable)
+ self.assert_(immutable not in x)
+ self.assert_(immutable2 not in x)
+ x.add(immutable2)
+ self.assert_(immutable not in x)
+ self.assert_(immutable2 in x)
+ x.add(immutable)
+ self.assert_(immutable in x)
+ self.assert_(immutable2 in x)
+
+
+class ImmutableTypeConversionDictTestCase(ImmutableDictBaseTestCase):
+ storage_class = datastructures.ImmutableTypeConversionDict
+
+
+class ImmutableMultiDictTestCase(ImmutableDictBaseTestCase):
+ storage_class = datastructures.ImmutableMultiDict
+
+ def test_multidict_is_hashable(self):
+ cls = self.storage_class
+ immutable = cls({'a': [1, 2], 'b': 2})
+ immutable2 = cls({'a': [1], 'b': 2})
+ x = set([immutable])
+ self.assert_(immutable in x)
+ self.assert_(immutable2 not in x)
+ x.discard(immutable)
+ self.assert_(immutable not in x)
+ self.assert_(immutable2 not in x)
+ x.add(immutable2)
+ self.assert_(immutable not in x)
+ self.assert_(immutable2 in x)
+ x.add(immutable)
+ self.assert_(immutable in x)
+ self.assert_(immutable2 in x)
+
+
+class ImmutableDictTestCase(ImmutableDictBaseTestCase):
+ storage_class = datastructures.ImmutableDict
+
+
+class ImmutableOrderedMultiDictTestCase(ImmutableDictBaseTestCase):
+ storage_class = datastructures.ImmutableOrderedMultiDict
+
+ def test_ordered_multidict_is_hashable(self):
+ a = self.storage_class([('a', 1), ('b', 1), ('a', 2)])
+ b = self.storage_class([('a', 1), ('a', 2), ('b', 1)])
+ self.assert_not_equal(hash(a), hash(b))
+
+
+class MultiDictTestCase(MutableMultiDictBaseTestCase):
+ storage_class = datastructures.MultiDict
+
+ def test_multidict_pop(self):
+ make_d = lambda: self.storage_class({'foo': [1, 2, 3, 4]})
+ d = make_d()
+ assert d.pop('foo') == 1
+ assert not d
+ d = make_d()
+ assert d.pop('foo', 32) == 1
+ assert not d
+ d = make_d()
+ assert d.pop('foos', 32) == 32
+ assert d
+
+ with self.assert_raises(KeyError):
+ d.pop('foos')
+
+ def test_setlistdefault(self):
+ md = self.storage_class()
+ assert md.setlistdefault('u', [-1, -2]) == [-1, -2]
+ assert md.getlist('u') == [-1, -2]
+ assert md['u'] == -1
+
+ def test_iter_interfaces(self):
+ mapping = [('a', 1), ('b', 2), ('a', 2), ('d', 3),
+ ('a', 1), ('a', 3), ('d', 4), ('c', 3)]
+ md = self.storage_class(mapping)
+ assert list(zip(md.keys(), md.listvalues())) == list(md.lists())
+ assert list(zip(md, md.iterlistvalues())) == list(md.iterlists())
+ assert list(zip(md.iterkeys(), md.iterlistvalues())) == list(md.iterlists())
+
+
+class OrderedMultiDictTestCase(MutableMultiDictBaseTestCase):
+ storage_class = datastructures.OrderedMultiDict
+
+ def test_ordered_interface(self):
+ cls = self.storage_class
+
+ d = cls()
+ assert not d
+ d.add('foo', 'bar')
+ assert len(d) == 1
+ d.add('foo', 'baz')
+ assert len(d) == 1
+ assert d.items() == [('foo', 'bar')]
+ assert list(d) == ['foo']
+ assert d.items(multi=True) == [('foo', 'bar'),
+ ('foo', 'baz')]
+ del d['foo']
+ assert not d
+ assert len(d) == 0
+ assert list(d) == []
+
+ d.update([('foo', 1), ('foo', 2), ('bar', 42)])
+ d.add('foo', 3)
+ assert d.getlist('foo') == [1, 2, 3]
+ assert d.getlist('bar') == [42]
+ assert d.items() == [('foo', 1), ('bar', 42)]
+ assert d.keys() == list(d) == list(d.iterkeys()) == ['foo', 'bar']
+ assert d.items(multi=True) == [('foo', 1), ('foo', 2),
+ ('bar', 42), ('foo', 3)]
+ assert len(d) == 2
+
+ assert d.pop('foo') == 1
+ assert d.pop('blafasel', None) is None
+ assert d.pop('blafasel', 42) == 42
+ assert len(d) == 1
+ assert d.poplist('bar') == [42]
+ assert not d
+
+ d.get('missingkey') is None
+
+ d.add('foo', 42)
+ d.add('foo', 23)
+ d.add('bar', 2)
+ d.add('foo', 42)
+ assert d == datastructures.MultiDict(d)
+ id = self.storage_class(d)
+ assert d == id
+ d.add('foo', 2)
+ assert d != id
+
+ d.update({'blah': [1, 2, 3]})
+ assert d['blah'] == 1
+ assert d.getlist('blah') == [1, 2, 3]
+
+ # setlist works
+ d = self.storage_class()
+ d['foo'] = 42
+ d.setlist('foo', [1, 2])
+ assert d.getlist('foo') == [1, 2]
+
+ with self.assert_raises(BadRequestKeyError):
+ d.pop('missing')
+ with self.assert_raises(BadRequestKeyError):
+ d['missing']
+
+ # popping
+ d = self.storage_class()
+ d.add('foo', 23)
+ d.add('foo', 42)
+ d.add('foo', 1)
+ assert d.popitem() == ('foo', 23)
+ with self.assert_raises(BadRequestKeyError):
+ d.popitem()
+ assert not d
+
+ d.add('foo', 23)
+ d.add('foo', 42)
+ d.add('foo', 1)
+ assert d.popitemlist() == ('foo', [23, 42, 1])
+
+ with self.assert_raises(BadRequestKeyError):
+ d.popitemlist()
+
+
+class CombinedMultiDictTestCase(WerkzeugTestCase):
+ storage_class = datastructures.CombinedMultiDict
+
+ def test_basic_interface(self):
+ d1 = datastructures.MultiDict([('foo', '1')])
+ d2 = datastructures.MultiDict([('bar', '2'), ('bar', '3')])
+ d = self.storage_class([d1, d2])
+
+ # lookup
+ assert d['foo'] == '1'
+ assert d['bar'] == '2'
+ assert d.getlist('bar') == ['2', '3']
+
+ assert sorted(d.items()) == [('bar', '2'), ('foo', '1')], d.items()
+ assert sorted(d.items(multi=True)) == [('bar', '2'), ('bar', '3'), ('foo', '1')]
+ assert 'missingkey' not in d
+ assert 'foo' in d
+
+ # type lookup
+ assert d.get('foo', type=int) == 1
+ assert d.getlist('bar', type=int) == [2, 3]
+
+ # get key errors for missing stuff
+ with self.assert_raises(KeyError):
+ d['missing']
+
+ # make sure that they are immutable
+ with self.assert_raises(TypeError):
+ d['foo'] = 'blub'
+
+ # copies are immutable
+ d = d.copy()
+ with self.assert_raises(TypeError):
+ d['foo'] = 'blub'
+
+ # make sure lists merges
+ md1 = datastructures.MultiDict((("foo", "bar"),))
+ md2 = datastructures.MultiDict((("foo", "blafasel"),))
+ x = self.storage_class((md1, md2))
+ assert x.lists() == [('foo', ['bar', 'blafasel'])]
+
+
+class HeadersTestCase(WerkzeugTestCase):
+ storage_class = datastructures.Headers
+
+ def test_basic_interface(self):
+ headers = self.storage_class()
+ headers.add('Content-Type', 'text/plain')
+ headers.add('X-Foo', 'bar')
+ assert 'x-Foo' in headers
+ assert 'Content-type' in headers
+
+ headers['Content-Type'] = 'foo/bar'
+ assert headers['Content-Type'] == 'foo/bar'
+ assert len(headers.getlist('Content-Type')) == 1
+
+ # list conversion
+ assert headers.to_list() == [
+ ('Content-Type', 'foo/bar'),
+ ('X-Foo', 'bar')
+ ]
+ assert str(headers) == (
+ "Content-Type: foo/bar\r\n"
+ "X-Foo: bar\r\n"
+ "\r\n")
+ assert str(self.storage_class()) == "\r\n"
+
+ # extended add
+ headers.add('Content-Disposition', 'attachment', filename='foo')
+ assert headers['Content-Disposition'] == 'attachment; filename=foo'
+
+ headers.add('x', 'y', z='"')
+ assert headers['x'] == r'y; z="\""'
+
+ def test_defaults_and_conversion(self):
+ # defaults
+ headers = self.storage_class([
+ ('Content-Type', 'text/plain'),
+ ('X-Foo', 'bar'),
+ ('X-Bar', '1'),
+ ('X-Bar', '2')
+ ])
+ assert headers.getlist('x-bar') == ['1', '2']
+ assert headers.get('x-Bar') == '1'
+ assert headers.get('Content-Type') == 'text/plain'
+
+ assert headers.setdefault('X-Foo', 'nope') == 'bar'
+ assert headers.setdefault('X-Bar', 'nope') == '1'
+ assert headers.setdefault('X-Baz', 'quux') == 'quux'
+ assert headers.setdefault('X-Baz', 'nope') == 'quux'
+ headers.pop('X-Baz')
+
+ # type conversion
+ assert headers.get('x-bar', type=int) == 1
+ assert headers.getlist('x-bar', type=int) == [1, 2]
+
+ # list like operations
+ assert headers[0] == ('Content-Type', 'text/plain')
+ assert headers[:1] == self.storage_class([('Content-Type', 'text/plain')])
+ del headers[:2]
+ del headers[-1]
+ assert headers == self.storage_class([('X-Bar', '1')])
+
+ def test_copying(self):
+ a = self.storage_class([('foo', 'bar')])
+ b = a.copy()
+ a.add('foo', 'baz')
+ assert a.getlist('foo') == ['bar', 'baz']
+ assert b.getlist('foo') == ['bar']
+
+ def test_popping(self):
+ headers = self.storage_class([('a', 1)])
+ assert headers.pop('a') == 1
+ assert headers.pop('b', 2) == 2
+
+ with self.assert_raises(KeyError):
+ headers.pop('c')
+
+ def test_set_arguments(self):
+ a = self.storage_class()
+ a.set('Content-Disposition', 'useless')
+ a.set('Content-Disposition', 'attachment', filename='foo')
+ assert a['Content-Disposition'] == 'attachment; filename=foo'
+
+ def test_reject_newlines(self):
+ h = self.storage_class()
+
+ for variation in 'foo\nbar', 'foo\r\nbar', 'foo\rbar':
+ with self.assert_raises(ValueError):
+ h['foo'] = variation
+ with self.assert_raises(ValueError):
+ h.add('foo', variation)
+ with self.assert_raises(ValueError):
+ h.add('foo', 'test', option=variation)
+ with self.assert_raises(ValueError):
+ h.set('foo', variation)
+ with self.assert_raises(ValueError):
+ h.set('foo', 'test', option=variation)
+
+
+class EnvironHeadersTestCase(WerkzeugTestCase):
+ storage_class = datastructures.EnvironHeaders
+
+ def test_basic_interface(self):
+ # this happens in multiple WSGI servers because they
+ # use a vary naive way to convert the headers;
+ broken_env = {
+ 'HTTP_CONTENT_TYPE': 'text/html',
+ 'CONTENT_TYPE': 'text/html',
+ 'HTTP_CONTENT_LENGTH': '0',
+ 'CONTENT_LENGTH': '0',
+ 'HTTP_ACCEPT': '*',
+ 'wsgi.version': (1, 0)
+ }
+ headers = self.storage_class(broken_env)
+ assert headers
+ assert len(headers) == 3
+ assert sorted(headers) == [
+ ('Accept', '*'),
+ ('Content-Length', '0'),
+ ('Content-Type', 'text/html')
+ ]
+ assert not self.storage_class({'wsgi.version': (1, 0)})
+ assert len(self.storage_class({'wsgi.version': (1, 0)})) == 0
+
+
+class HeaderSetTestCase(WerkzeugTestCase):
+ storage_class = datastructures.HeaderSet
+
+ def test_basic_interface(self):
+ hs = self.storage_class()
+ hs.add('foo')
+ hs.add('bar')
+ assert 'Bar' in hs
+ assert hs.find('foo') == 0
+ assert hs.find('BAR') == 1
+ assert hs.find('baz') < 0
+ hs.discard('missing')
+ hs.discard('foo')
+ assert hs.find('foo') < 0
+ assert hs.find('bar') == 0
+
+ with self.assert_raises(IndexError):
+ hs.index('missing')
+
+ assert hs.index('bar') == 0
+ assert hs
+ hs.clear()
+ assert not hs
+
+
+class ImmutableListTestCase(WerkzeugTestCase):
+ storage_class = datastructures.ImmutableList
+
+ def test_list_hashable(self):
+ t = (1, 2, 3, 4)
+ l = self.storage_class(t)
+ self.assert_equal(hash(t), hash(l))
+ self.assert_not_equal(t, l)
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(MultiDictTestCase))
+ suite.addTest(unittest.makeSuite(OrderedMultiDictTestCase))
+ suite.addTest(unittest.makeSuite(CombinedMultiDictTestCase))
+ suite.addTest(unittest.makeSuite(ImmutableTypeConversionDictTestCase))
+ suite.addTest(unittest.makeSuite(ImmutableMultiDictTestCase))
+ suite.addTest(unittest.makeSuite(ImmutableDictTestCase))
+ suite.addTest(unittest.makeSuite(ImmutableOrderedMultiDictTestCase))
+ suite.addTest(unittest.makeSuite(HeadersTestCase))
+ suite.addTest(unittest.makeSuite(EnvironHeadersTestCase))
+ suite.addTest(unittest.makeSuite(HeaderSetTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/debug.py b/websdk/werkzeug/testsuite/debug.py
new file mode 100644
index 0000000..ffe9a2e
--- /dev/null
+++ b/websdk/werkzeug/testsuite/debug.py
@@ -0,0 +1,163 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.debug
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests some debug utilities.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+import sys
+import re
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug.debug.repr import debug_repr, DebugReprGenerator, \
+ dump, helper
+from werkzeug.debug.console import HTMLStringO
+
+
+class DebugReprTestCase(WerkzeugTestCase):
+
+ def test_basic_repr(self):
+ assert debug_repr([]) == u'[]'
+ assert debug_repr([1, 2]) == \
+ u'[<span class="number">1</span>, <span class="number">2</span>]'
+ assert debug_repr([1, 'test']) == \
+ u'[<span class="number">1</span>, <span class="string">\'test\'</span>]'
+ assert debug_repr([None]) == \
+ u'[<span class="object">None</span>]'
+
+ def test_sequence_repr(self):
+ assert debug_repr(list(range(20))) == (
+ u'[<span class="number">0</span>, <span class="number">1</span>, '
+ u'<span class="number">2</span>, <span class="number">3</span>, '
+ u'<span class="number">4</span>, <span class="number">5</span>, '
+ u'<span class="number">6</span>, <span class="number">7</span>, '
+ u'<span class="extended"><span class="number">8</span>, '
+ u'<span class="number">9</span>, <span class="number">10</span>, '
+ u'<span class="number">11</span>, <span class="number">12</span>, '
+ u'<span class="number">13</span>, <span class="number">14</span>, '
+ u'<span class="number">15</span>, <span class="number">16</span>, '
+ u'<span class="number">17</span>, <span class="number">18</span>, '
+ u'<span class="number">19</span></span>]'
+ )
+
+ def test_mapping_repr(self):
+ assert debug_repr({}) == u'{}'
+ assert debug_repr({'foo': 42}) == \
+ u'{<span class="pair"><span class="key"><span class="string">\'foo\''\
+ u'</span></span>: <span class="value"><span class="number">42' \
+ u'</span></span></span>}'
+ assert debug_repr(dict(zip(range(10), [None] * 10))) == \
+ u'{<span class="pair"><span class="key"><span class="number">0</span></span>: <span class="value"><span class="object">None</span></span></span>, <span class="pair"><span class="key"><span class="number">1</span></span>: <span class="value"><span class="object">None</span></span></span>, <span class="pair"><span class="key"><span class="number">2</span></span>: <span class="value"><span class="object">None</span></span></span>, <span class="pair"><span class="key"><span class="number">3</span></span>: <span class="value"><span class="object">None</span></span></span>, <span class="extended"><span class="pair"><span class="key"><span class="number">4</span></span>: <span class="value"><span class="object">None</span></span></span>, <span class="pair"><span class="key"><span class="number">5</span></span>: <span class="value"><span class="object">None</span></span></span>, <span class="pair"><span class="key"><span class="number">6</span></span>: <span class="value"><span class="object">None</span></span></span>, <span class="pair"><span class="key"><span class="number">7</span></span>: <span class="value"><span class="object">None</span></span></span>, <span class="pair"><span class="key"><span class="number">8</span></span>: <span class="value"><span class="object">None</span></span></span>, <span class="pair"><span class="key"><span class="number">9</span></span>: <span class="value"><span class="object">None</span></span></span></span>}'
+ assert debug_repr((1, 'zwei', u'drei')) ==\
+ u'(<span class="number">1</span>, <span class="string">\'' \
+ u'zwei\'</span>, <span class="string">u\'drei\'</span>)'
+
+ def test_custom_repr(self):
+ class Foo(object):
+ def __repr__(self):
+ return '<Foo 42>'
+ assert debug_repr(Foo()) == '<span class="object">&lt;Foo 42&gt;</span>'
+
+ def test_list_subclass_repr(self):
+ class MyList(list):
+ pass
+ assert debug_repr(MyList([1, 2])) == \
+ u'<span class="module">werkzeug.testsuite.debug.</span>MyList([' \
+ u'<span class="number">1</span>, <span class="number">2</span>])'
+
+ def test_regex_repr(self):
+ assert debug_repr(re.compile(r'foo\d')) == \
+ u're.compile(<span class="string regex">r\'foo\\d\'</span>)'
+ assert debug_repr(re.compile(ur'foo\d')) == \
+ u're.compile(<span class="string regex">ur\'foo\\d\'</span>)'
+
+ def test_set_repr(self):
+ assert debug_repr(frozenset('x')) == \
+ u'frozenset([<span class="string">\'x\'</span>])'
+ assert debug_repr(set('x')) == \
+ u'set([<span class="string">\'x\'</span>])'
+
+ def test_recursive_repr(self):
+ a = [1]
+ a.append(a)
+ assert debug_repr(a) == u'[<span class="number">1</span>, [...]]'
+
+ def test_broken_repr(self):
+ class Foo(object):
+ def __repr__(self):
+ 1/0
+
+ assert debug_repr(Foo()) == \
+ u'<span class="brokenrepr">&lt;broken repr (ZeroDivisionError: ' \
+ u'integer division or modulo by zero)&gt;</span>'
+
+
+class DebugHelpersTestCase(WerkzeugTestCase):
+
+ def test_object_dumping(self):
+ class Foo(object):
+ x = 42
+ y = 23
+ def __init__(self):
+ self.z = 15
+
+ drg = DebugReprGenerator()
+ out = drg.dump_object(Foo())
+ assert re.search('Details for werkzeug.testsuite.debug.Foo object at', out)
+ assert re.search('<th>x.*<span class="number">42</span>(?s)', out)
+ assert re.search('<th>y.*<span class="number">23</span>(?s)', out)
+ assert re.search('<th>z.*<span class="number">15</span>(?s)', out)
+
+ out = drg.dump_object({'x': 42, 'y': 23})
+ assert re.search('Contents of', out)
+ assert re.search('<th>x.*<span class="number">42</span>(?s)', out)
+ assert re.search('<th>y.*<span class="number">23</span>(?s)', out)
+
+ out = drg.dump_object({'x': 42, 'y': 23, 23: 11})
+ assert not re.search('Contents of', out)
+
+ out = drg.dump_locals({'x': 42, 'y': 23})
+ assert re.search('Local variables in frame', out)
+ assert re.search('<th>x.*<span class="number">42</span>(?s)', out)
+ assert re.search('<th>y.*<span class="number">23</span>(?s)', out)
+
+ def test_debug_dump(self):
+ old = sys.stdout
+ sys.stdout = HTMLStringO()
+ try:
+ dump([1, 2, 3])
+ x = sys.stdout.reset()
+ dump()
+ y = sys.stdout.reset()
+ finally:
+ sys.stdout = old
+
+ assert 'Details for list object at' in x
+ assert '<span class="number">1</span>' in x
+ assert 'Local variables in frame' in y
+ assert '<th>x' in y
+ assert '<th>old' in y
+
+ def test_debug_help(self):
+ old = sys.stdout
+ sys.stdout = HTMLStringO()
+ try:
+ helper([1, 2, 3])
+ x = sys.stdout.reset()
+ finally:
+ sys.stdout = old
+
+ assert 'Help on list object' in x
+ assert '__delitem__' in x
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(DebugReprTestCase))
+ suite.addTest(unittest.makeSuite(DebugHelpersTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/exceptions.py b/websdk/werkzeug/testsuite/exceptions.py
new file mode 100644
index 0000000..8530b9c
--- /dev/null
+++ b/websdk/werkzeug/testsuite/exceptions.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.exceptions
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ The tests for the exception classes.
+
+ TODO:
+
+ - This is undertested. HTML is never checked
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug import exceptions
+from werkzeug.wrappers import Response
+
+
+class ExceptionsTestCase(WerkzeugTestCase):
+
+ def test_proxy_exception(self):
+ """Proxy exceptions"""
+ orig_resp = Response('Hello World')
+ try:
+ exceptions.abort(orig_resp)
+ except exceptions.HTTPException, e:
+ resp = e.get_response({})
+ else:
+ self.fail('exception not raised')
+ self.assert_(resp is orig_resp)
+ self.assert_equal(resp.data, 'Hello World')
+
+ def test_aborter(self):
+ """Exception aborter"""
+ abort = exceptions.abort
+ self.assert_raises(exceptions.BadRequest, abort, 400)
+ self.assert_raises(exceptions.Unauthorized, abort, 401)
+ self.assert_raises(exceptions.Forbidden, abort, 403)
+ self.assert_raises(exceptions.NotFound, abort, 404)
+ self.assert_raises(exceptions.MethodNotAllowed, abort, 405, ['GET', 'HEAD'])
+ self.assert_raises(exceptions.NotAcceptable, abort, 406)
+ self.assert_raises(exceptions.RequestTimeout, abort, 408)
+ self.assert_raises(exceptions.Gone, abort, 410)
+ self.assert_raises(exceptions.LengthRequired, abort, 411)
+ self.assert_raises(exceptions.PreconditionFailed, abort, 412)
+ self.assert_raises(exceptions.RequestEntityTooLarge, abort, 413)
+ self.assert_raises(exceptions.RequestURITooLarge, abort, 414)
+ self.assert_raises(exceptions.UnsupportedMediaType, abort, 415)
+ self.assert_raises(exceptions.InternalServerError, abort, 500)
+ self.assert_raises(exceptions.NotImplemented, abort, 501)
+ self.assert_raises(exceptions.BadGateway, abort, 502)
+ self.assert_raises(exceptions.ServiceUnavailable, abort, 503)
+
+ myabort = exceptions.Aborter({1: exceptions.NotFound})
+ self.assert_raises(LookupError, myabort, 404)
+ self.assert_raises(exceptions.NotFound, myabort, 1)
+
+ myabort = exceptions.Aborter(extra={1: exceptions.NotFound})
+ self.assert_raises(exceptions.NotFound, myabort, 404)
+ self.assert_raises(exceptions.NotFound, myabort, 1)
+
+ def test_exception_repr(self):
+ exc = exceptions.NotFound()
+ self.assert_equal(unicode(exc), '404: Not Found')
+ self.assert_equal(repr(exc), "<NotFound '404: Not Found'>")
+
+ exc = exceptions.NotFound('Not There')
+ self.assert_equal(unicode(exc), '404: Not There')
+ self.assert_equal(repr(exc), "<NotFound '404: Not There'>")
+
+ def test_special_exceptions(self):
+ exc = exceptions.MethodNotAllowed(['GET', 'HEAD', 'POST'])
+ h = dict(exc.get_headers({}))
+ self.assert_equal(h['Allow'], 'GET, HEAD, POST')
+ self.assert_('The method DELETE is not allowed' in exc.get_description({
+ 'REQUEST_METHOD': 'DELETE'
+ }))
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ExceptionsTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/formparser.py b/websdk/werkzeug/testsuite/formparser.py
new file mode 100644
index 0000000..7dd5b79
--- /dev/null
+++ b/websdk/werkzeug/testsuite/formparser.py
@@ -0,0 +1,370 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.formparser
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the form parsing facilities.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import unittest
+from StringIO import StringIO
+from os.path import join, dirname
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug import formparser
+from werkzeug.test import create_environ, Client
+from werkzeug.wrappers import Request, Response
+from werkzeug.exceptions import RequestEntityTooLarge
+
+
+@Request.application
+def form_data_consumer(request):
+ result_object = request.args['object']
+ if result_object == 'text':
+ return Response(repr(request.form['text']))
+ f = request.files[result_object]
+ return Response('\n'.join((
+ repr(f.filename),
+ repr(f.name),
+ repr(f.content_type),
+ f.stream.read()
+ )))
+
+
+def get_contents(filename):
+ f = file(filename, 'rb')
+ try:
+ return f.read()
+ finally:
+ f.close()
+
+
+class FormParserTestCase(WerkzeugTestCase):
+
+ def test_limiting(self):
+ """Test the limiting features"""
+ data = 'foo=Hello+World&bar=baz'
+ req = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='application/x-www-form-urlencoded',
+ method='POST')
+ req.max_content_length = 400
+ self.assert_equal(req.form['foo'], 'Hello World')
+
+ req = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='application/x-www-form-urlencoded',
+ method='POST')
+ req.max_form_memory_size = 7
+ self.assert_raises(RequestEntityTooLarge, lambda: req.form['foo'])
+
+ req = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='application/x-www-form-urlencoded',
+ method='POST')
+ req.max_form_memory_size = 400
+ self.assert_equal(req.form['foo'], 'Hello World')
+
+ data = ('--foo\r\nContent-Disposition: form-field; name=foo\r\n\r\n'
+ 'Hello World\r\n'
+ '--foo\r\nContent-Disposition: form-field; name=bar\r\n\r\n'
+ 'bar=baz\r\n--foo--')
+ req = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; boundary=foo',
+ method='POST')
+ req.max_content_length = 4
+ self.assert_raises(RequestEntityTooLarge, lambda: req.form['foo'])
+
+ req = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; boundary=foo',
+ method='POST')
+ req.max_content_length = 400
+ self.assert_equal(req.form['foo'], 'Hello World')
+
+ req = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; boundary=foo',
+ method='POST')
+ req.max_form_memory_size = 7
+ self.assert_raises(RequestEntityTooLarge, lambda: req.form['foo'])
+
+ req = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; boundary=foo',
+ method='POST')
+ req.max_form_memory_size = 400
+ self.assert_equal(req.form['foo'], 'Hello World')
+
+ def test_parse_form_data_put_without_content(self):
+ """A PUT without a Content-Type header returns empty data
+
+ Both rfc1945 and rfc2616 (1.0 and 1.1) say "Any HTTP/[1.0/1.1] message
+ containing an entity-body SHOULD include a Content-Type header field
+ defining the media type of that body." In the case where either
+ headers are omitted, parse_form_data should still work.
+ """
+ env = create_environ('/foo', 'http://example.org/', method='PUT')
+ del env['CONTENT_TYPE']
+ del env['CONTENT_LENGTH']
+
+ stream, form, files = formparser.parse_form_data(env)
+ self.assert_equal(stream.read(), '')
+ self.assert_equal(len(form), 0)
+ self.assert_equal(len(files), 0)
+
+ def test_parse_form_data_get_without_content(self):
+ """GET requests without data, content type and length returns no data"""
+ env = create_environ('/foo', 'http://example.org/', method='GET')
+ del env['CONTENT_TYPE']
+ del env['CONTENT_LENGTH']
+
+ stream, form, files = formparser.parse_form_data(env)
+ self.assert_equal(stream.read(), '')
+ self.assert_equal(len(form), 0)
+ self.assert_equal(len(files), 0)
+
+ def test_large_file(self):
+ """Test a largish file."""
+ data = 'x' * (1024 * 600)
+ req = Request.from_values(data={'foo': (StringIO(data), 'test.txt')},
+ method='POST')
+ # make sure we have a real file here, because we expect to be
+ # on the disk. > 1024 * 500
+ self.assert_(isinstance(req.files['foo'].stream, file))
+
+
+class MultiPartTestCase(WerkzeugTestCase):
+
+ def test_basic(self):
+ """Tests multipart parsing against data collected from webbrowsers"""
+ resources = join(dirname(__file__), 'multipart')
+ client = Client(form_data_consumer, Response)
+
+ repository = [
+ ('firefox3-2png1txt', '---------------------------186454651713519341951581030105', [
+ (u'anchor.png', 'file1', 'image/png', 'file1.png'),
+ (u'application_edit.png', 'file2', 'image/png', 'file2.png')
+ ], u'example text'),
+ ('firefox3-2pnglongtext', '---------------------------14904044739787191031754711748', [
+ (u'accept.png', 'file1', 'image/png', 'file1.png'),
+ (u'add.png', 'file2', 'image/png', 'file2.png')
+ ], u'--long text\r\n--with boundary\r\n--lookalikes--'),
+ ('opera8-2png1txt', '----------zEO9jQKmLc2Cq88c23Dx19', [
+ (u'arrow_branch.png', 'file1', 'image/png', 'file1.png'),
+ (u'award_star_bronze_1.png', 'file2', 'image/png', 'file2.png')
+ ], u'blafasel öäü'),
+ ('webkit3-2png1txt', '----WebKitFormBoundaryjdSFhcARk8fyGNy6', [
+ (u'gtk-apply.png', 'file1', 'image/png', 'file1.png'),
+ (u'gtk-no.png', 'file2', 'image/png', 'file2.png')
+ ], u'this is another text with ümläüts'),
+ ('ie6-2png1txt', '---------------------------7d91b03a20128', [
+ (u'file1.png', 'file1', 'image/x-png', 'file1.png'),
+ (u'file2.png', 'file2', 'image/x-png', 'file2.png')
+ ], u'ie6 sucks :-/')
+ ]
+
+ for name, boundary, files, text in repository:
+ folder = join(resources, name)
+ data = get_contents(join(folder, 'request.txt'))
+ for filename, field, content_type, fsname in files:
+ response = client.post('/?object=' + field, data=data, content_type=
+ 'multipart/form-data; boundary="%s"' % boundary,
+ content_length=len(data))
+ lines = response.data.split('\n', 3)
+ self.assert_equal(lines[0], repr(filename))
+ self.assert_equal(lines[1], repr(field))
+ self.assert_equal(lines[2], repr(content_type))
+ self.assert_equal(lines[3], get_contents(join(folder, fsname)))
+ response = client.post('/?object=text', data=data, content_type=
+ 'multipart/form-data; boundary="%s"' % boundary,
+ content_length=len(data))
+ self.assert_equal(response.data, repr(text))
+
+ def test_ie7_unc_path(self):
+ client = Client(form_data_consumer, Response)
+ data_file = join(dirname(__file__), 'multipart', 'ie7_full_path_request.txt')
+ data = get_contents(data_file)
+ boundary = '---------------------------7da36d1b4a0164'
+ response = client.post('/?object=cb_file_upload_multiple', data=data, content_type=
+ 'multipart/form-data; boundary="%s"' % boundary, content_length=len(data))
+ lines = response.data.split('\n', 3)
+ self.assert_equal(lines[0],
+ repr(u'Sellersburg Town Council Meeting 02-22-2010doc.doc'))
+
+ def test_end_of_file(self):
+ """Test for multipart files ending unexpectedly"""
+ # This test looks innocent but it was actually timeing out in
+ # the Werkzeug 0.5 release version (#394)
+ data = (
+ '--foo\r\n'
+ 'Content-Disposition: form-data; name="test"; filename="test.txt"\r\n'
+ 'Content-Type: text/plain\r\n\r\n'
+ 'file contents and no end'
+ )
+ data = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; boundary=foo',
+ method='POST')
+ self.assert_(not data.files)
+ self.assert_(not data.form)
+
+ def test_broken(self):
+ """Broken multipart does not break the applicaiton"""
+ data = (
+ '--foo\r\n'
+ 'Content-Disposition: form-data; name="test"; filename="test.txt"\r\n'
+ 'Content-Transfer-Encoding: base64\r\n'
+ 'Content-Type: text/plain\r\n\r\n'
+ 'broken base 64'
+ '--foo--'
+ )
+ _, form, files = formparser.parse_form_data(create_environ(data=data,
+ method='POST', content_type='multipart/form-data; boundary=foo'))
+ self.assert_(not files)
+ self.assert_(not form)
+
+ self.assert_raises(ValueError, formparser.parse_form_data,
+ create_environ(data=data, method='POST',
+ content_type='multipart/form-data; boundary=foo'),
+ silent=False)
+
+ def test_file_no_content_type(self):
+ """Chrome does not always provide a content type."""
+ data = (
+ '--foo\r\n'
+ 'Content-Disposition: form-data; name="test"; filename="test.txt"\r\n\r\n'
+ 'file contents\r\n--foo--'
+ )
+ data = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; boundary=foo',
+ method='POST')
+ self.assert_equal(data.files['test'].filename, 'test.txt')
+ self.assert_equal(data.files['test'].read(), 'file contents')
+
+ def test_extra_newline(self):
+ """Test for multipart uploads with extra newlines"""
+ # this test looks innocent but it was actually timeing out in
+ # the Werkzeug 0.5 release version (#394)
+ data = (
+ '\r\n\r\n--foo\r\n'
+ 'Content-Disposition: form-data; name="foo"\r\n\r\n'
+ 'a string\r\n'
+ '--foo--'
+ )
+ data = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; boundary=foo',
+ method='POST')
+ self.assert_(not data.files)
+ self.assert_equal(data.form['foo'], 'a string')
+
+ def test_headers(self):
+ """Test access to multipart headers"""
+ data = ('--foo\r\n'
+ 'Content-Disposition: form-data; name="foo"; filename="foo.txt"\r\n'
+ 'X-Custom-Header: blah\r\n'
+ 'Content-Type: text/plain; charset=utf-8\r\n\r\n'
+ 'file contents, just the contents\r\n'
+ '--foo--')
+ req = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; boundary=foo',
+ method='POST')
+ foo = req.files['foo']
+ self.assert_equal(foo.mimetype, 'text/plain')
+ self.assert_equal(foo.mimetype_params, {'charset': 'utf-8'})
+ self.assert_equal(foo.headers['content-type'], foo.content_type)
+ self.assert_equal(foo.content_type, 'text/plain; charset=utf-8')
+ self.assert_equal(foo.headers['x-custom-header'], 'blah')
+
+ def test_nonstandard_line_endings(self):
+ """Test nonstandard line endings of multipart form data"""
+ for nl in '\n', '\r', '\r\n':
+ data = nl.join((
+ '--foo',
+ 'Content-Disposition: form-data; name=foo',
+ '',
+ 'this is just bar',
+ '--foo',
+ 'Content-Disposition: form-data; name=bar',
+ '',
+ 'blafasel',
+ '--foo--'
+ ))
+ req = Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; '
+ 'boundary=foo', method='POST')
+ self.assert_equal(req.form['foo'], 'this is just bar')
+ self.assert_equal(req.form['bar'], 'blafasel')
+
+ def test_failures(self):
+ def parse_multipart(stream, boundary, content_length):
+ parser = formparser.MultiPartParser(content_length)
+ return parser.parse(stream, boundary, content_length)
+ self.assert_raises(ValueError, parse_multipart, StringIO(''), '', 0)
+ self.assert_raises(ValueError, parse_multipart, StringIO(''), 'broken ', 0)
+
+ data = '--foo\r\n\r\nHello World\r\n--foo--'
+ self.assert_raises(ValueError, parse_multipart, StringIO(data), 'foo', len(data))
+
+ data = '--foo\r\nContent-Disposition: form-field; name=foo\r\n' \
+ 'Content-Transfer-Encoding: base64\r\n\r\nHello World\r\n--foo--'
+ self.assert_raises(ValueError, parse_multipart, StringIO(data), 'foo', len(data))
+
+ data = '--foo\r\nContent-Disposition: form-field; name=foo\r\n\r\nHello World\r\n'
+ self.assert_raises(ValueError, parse_multipart, StringIO(data), 'foo', len(data))
+
+ x = formparser.parse_multipart_headers(['foo: bar\r\n', ' x test\r\n'])
+ self.assert_equal(x['foo'], 'bar\n x test')
+ self.assert_raises(ValueError, formparser.parse_multipart_headers,
+ ['foo: bar\r\n', ' x test'])
+
+ def test_bad_newline_bad_newline_assumption(self):
+ """Make sure we don't eat up stuff that is not a newline"""
+ class ISORequest(Request):
+ charset = 'latin1'
+ contents = 'U2vlbmUgbORu'
+ data = '--foo\r\nContent-Disposition: form-data; name="test"\r\n' \
+ 'Content-Transfer-Encoding: base64\r\n\r\n' + \
+ contents + '\r\n--foo--'
+ req = ISORequest.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; boundary=foo',
+ method='POST')
+ self.assert_equal(req.form['test'], u'Sk\xe5ne l\xe4n')
+
+
+class InternalFunctionsTestCase(WerkzeugTestCase):
+
+ def test_lien_parser(self):
+ assert formparser._line_parse('foo') == ('foo', False)
+ assert formparser._line_parse('foo\r\n') == ('foo', True)
+ assert formparser._line_parse('foo\r') == ('foo', True)
+ assert formparser._line_parse('foo\n') == ('foo', True)
+
+ def test_find_terminator(self):
+ lineiter = iter('\n\n\nfoo\nbar\nbaz'.splitlines(True))
+ find_terminator = formparser.MultiPartParser()._find_terminator
+ line = find_terminator(lineiter)
+ assert line == 'foo'
+ assert list(lineiter) == ['bar\n', 'baz']
+ assert find_terminator([]) == ''
+ assert find_terminator(['']) == ''
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(FormParserTestCase))
+ suite.addTest(unittest.makeSuite(MultiPartTestCase))
+ suite.addTest(unittest.makeSuite(InternalFunctionsTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/http.py b/websdk/werkzeug/testsuite/http.py
new file mode 100644
index 0000000..733aee2
--- /dev/null
+++ b/websdk/werkzeug/testsuite/http.py
@@ -0,0 +1,392 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.http
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ HTTP parsing utilities.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+from datetime import datetime
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug import http, datastructures
+from werkzeug.test import create_environ
+
+
+class HTTPUtilityTestCase(WerkzeugTestCase):
+
+ def test_accept(self):
+ a = http.parse_accept_header('en-us,ru;q=0.5')
+ self.assert_equal(a.values(), ['en-us', 'ru'])
+ self.assert_equal(a.best, 'en-us')
+ self.assert_equal(a.find('ru'), 1)
+ self.assert_raises(ValueError, a.index, 'de')
+ self.assert_equal(a.to_header(), 'en-us,ru;q=0.5')
+
+ def test_mime_accept(self):
+ a = http.parse_accept_header('text/xml,application/xml,'
+ 'application/xhtml+xml,'
+ 'text/html;q=0.9,text/plain;q=0.8,'
+ 'image/png,*/*;q=0.5',
+ datastructures.MIMEAccept)
+ self.assert_raises(ValueError, lambda: a['missing'])
+ self.assert_equal(a['image/png'], 1)
+ self.assert_equal(a['text/plain'], 0.8)
+ self.assert_equal(a['foo/bar'], 0.5)
+ self.assert_equal(a[a.find('foo/bar')], ('*/*', 0.5))
+
+ def test_accept_matches(self):
+ a = http.parse_accept_header('text/xml,application/xml,application/xhtml+xml,'
+ 'text/html;q=0.9,text/plain;q=0.8,'
+ 'image/png', datastructures.MIMEAccept)
+ self.assert_equal(a.best_match(['text/html', 'application/xhtml+xml']),
+ 'application/xhtml+xml')
+ self.assert_equal(a.best_match(['text/html']), 'text/html')
+ self.assert_(a.best_match(['foo/bar']) is None)
+ self.assert_equal(a.best_match(['foo/bar', 'bar/foo'],
+ default='foo/bar'), 'foo/bar')
+ self.assert_equal(a.best_match(['application/xml', 'text/xml']), 'application/xml')
+
+ def test_charset_accept(self):
+ a = http.parse_accept_header('ISO-8859-1,utf-8;q=0.7,*;q=0.7',
+ datastructures.CharsetAccept)
+ self.assert_equal(a['iso-8859-1'], a['iso8859-1'])
+ self.assert_equal(a['iso-8859-1'], 1)
+ self.assert_equal(a['UTF8'], 0.7)
+ self.assert_equal(a['ebcdic'], 0.7)
+
+ def test_language_accept(self):
+ a = http.parse_accept_header('de-AT,de;q=0.8,en;q=0.5',
+ datastructures.LanguageAccept)
+ self.assert_equal(a.best, 'de-AT')
+ self.assert_('de_AT' in a)
+ self.assert_('en' in a)
+ self.assert_equal(a['de-at'], 1)
+ self.assert_equal(a['en'], 0.5)
+
+ def test_set_header(self):
+ hs = http.parse_set_header('foo, Bar, "Blah baz", Hehe')
+ self.assert_('blah baz' in hs)
+ self.assert_('foobar' not in hs)
+ self.assert_('foo' in hs)
+ self.assert_equal(list(hs), ['foo', 'Bar', 'Blah baz', 'Hehe'])
+ hs.add('Foo')
+ self.assert_equal(hs.to_header(), 'foo, Bar, "Blah baz", Hehe')
+
+ def test_list_header(self):
+ hl = http.parse_list_header('foo baz, blah')
+ self.assert_equal(hl, ['foo baz', 'blah'])
+
+ def test_dict_header(self):
+ d = http.parse_dict_header('foo="bar baz", blah=42')
+ self.assert_equal(d, {'foo': 'bar baz', 'blah': '42'})
+
+ def test_cache_control_header(self):
+ cc = http.parse_cache_control_header('max-age=0, no-cache')
+ assert cc.max_age == 0
+ assert cc.no_cache
+ cc = http.parse_cache_control_header('private, community="UCI"', None,
+ datastructures.ResponseCacheControl)
+ assert cc.private
+ assert cc['community'] == 'UCI'
+
+ c = datastructures.ResponseCacheControl()
+ assert c.no_cache is None
+ assert c.private is None
+ c.no_cache = True
+ assert c.no_cache == '*'
+ c.private = True
+ assert c.private == '*'
+ del c.private
+ assert c.private is None
+ assert c.to_header() == 'no-cache'
+
+ def test_authorization_header(self):
+ a = http.parse_authorization_header('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==')
+ assert a.type == 'basic'
+ assert a.username == 'Aladdin'
+ assert a.password == 'open sesame'
+
+ a = http.parse_authorization_header('''Digest username="Mufasa",
+ realm="testrealm@host.invalid",
+ nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+ uri="/dir/index.html",
+ qop=auth,
+ nc=00000001,
+ cnonce="0a4f113b",
+ response="6629fae49393a05397450978507c4ef1",
+ opaque="5ccc069c403ebaf9f0171e9517f40e41"''')
+ assert a.type == 'digest'
+ assert a.username == 'Mufasa'
+ assert a.realm == 'testrealm@host.invalid'
+ assert a.nonce == 'dcd98b7102dd2f0e8b11d0f600bfb0c093'
+ assert a.uri == '/dir/index.html'
+ assert 'auth' in a.qop
+ assert a.nc == '00000001'
+ assert a.cnonce == '0a4f113b'
+ assert a.response == '6629fae49393a05397450978507c4ef1'
+ assert a.opaque == '5ccc069c403ebaf9f0171e9517f40e41'
+
+ a = http.parse_authorization_header('''Digest username="Mufasa",
+ realm="testrealm@host.invalid",
+ nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+ uri="/dir/index.html",
+ response="e257afa1414a3340d93d30955171dd0e",
+ opaque="5ccc069c403ebaf9f0171e9517f40e41"''')
+ assert a.type == 'digest'
+ assert a.username == 'Mufasa'
+ assert a.realm == 'testrealm@host.invalid'
+ assert a.nonce == 'dcd98b7102dd2f0e8b11d0f600bfb0c093'
+ assert a.uri == '/dir/index.html'
+ assert a.response == 'e257afa1414a3340d93d30955171dd0e'
+ assert a.opaque == '5ccc069c403ebaf9f0171e9517f40e41'
+
+ assert http.parse_authorization_header('') is None
+ assert http.parse_authorization_header(None) is None
+ assert http.parse_authorization_header('foo') is None
+
+ def test_www_authenticate_header(self):
+ wa = http.parse_www_authenticate_header('Basic realm="WallyWorld"')
+ assert wa.type == 'basic'
+ assert wa.realm == 'WallyWorld'
+ wa.realm = 'Foo Bar'
+ assert wa.to_header() == 'Basic realm="Foo Bar"'
+
+ wa = http.parse_www_authenticate_header('''Digest
+ realm="testrealm@host.com",
+ qop="auth,auth-int",
+ nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+ opaque="5ccc069c403ebaf9f0171e9517f40e41"''')
+ assert wa.type == 'digest'
+ assert wa.realm == 'testrealm@host.com'
+ assert 'auth' in wa.qop
+ assert 'auth-int' in wa.qop
+ assert wa.nonce == 'dcd98b7102dd2f0e8b11d0f600bfb0c093'
+ assert wa.opaque == '5ccc069c403ebaf9f0171e9517f40e41'
+
+ wa = http.parse_www_authenticate_header('broken')
+ assert wa.type == 'broken'
+
+ assert not http.parse_www_authenticate_header('').type
+ assert not http.parse_www_authenticate_header('')
+
+ def test_etags(self):
+ assert http.quote_etag('foo') == '"foo"'
+ assert http.quote_etag('foo', True) == 'w/"foo"'
+ assert http.unquote_etag('"foo"') == ('foo', False)
+ assert http.unquote_etag('w/"foo"') == ('foo', True)
+ es = http.parse_etags('"foo", "bar", w/"baz", blar')
+ assert sorted(es) == ['bar', 'blar', 'foo']
+ assert 'foo' in es
+ assert 'baz' not in es
+ assert es.contains_weak('baz')
+ assert 'blar' in es
+ assert es.contains_raw('w/"baz"')
+ assert es.contains_raw('"foo"')
+ assert sorted(es.to_header().split(', ')) == ['"bar"', '"blar"', '"foo"', 'w/"baz"']
+
+ def test_parse_date(self):
+ assert http.parse_date('Sun, 06 Nov 1994 08:49:37 GMT ') == datetime(1994, 11, 6, 8, 49, 37)
+ assert http.parse_date('Sunday, 06-Nov-94 08:49:37 GMT') == datetime(1994, 11, 6, 8, 49, 37)
+ assert http.parse_date(' Sun Nov 6 08:49:37 1994') == datetime(1994, 11, 6, 8, 49, 37)
+ assert http.parse_date('foo') is None
+
+ def test_parse_date_overflows(self):
+ assert http.parse_date(' Sun 02 Feb 1343 08:49:37 GMT') == datetime(1343, 2, 2, 8, 49, 37)
+ assert http.parse_date('Thu, 01 Jan 1970 00:00:00 GMT') == datetime(1970, 1, 1, 0, 0)
+ assert http.parse_date('Thu, 33 Jan 1970 00:00:00 GMT') is None
+
+ def test_remove_entity_headers(self):
+ now = http.http_date()
+ headers1 = [('Date', now), ('Content-Type', 'text/html'), ('Content-Length', '0')]
+ headers2 = datastructures.Headers(headers1)
+
+ http.remove_entity_headers(headers1)
+ assert headers1 == [('Date', now)]
+
+ http.remove_entity_headers(headers2)
+ assert headers2 == datastructures.Headers([('Date', now)])
+
+ def test_remove_hop_by_hop_headers(self):
+ headers1 = [('Connection', 'closed'), ('Foo', 'bar'),
+ ('Keep-Alive', 'wtf')]
+ headers2 = datastructures.Headers(headers1)
+
+ http.remove_hop_by_hop_headers(headers1)
+ assert headers1 == [('Foo', 'bar')]
+
+ http.remove_hop_by_hop_headers(headers2)
+ assert headers2 == datastructures.Headers([('Foo', 'bar')])
+
+ def test_parse_options_header(self):
+ assert http.parse_options_header('something; foo="other\"thing"') == \
+ ('something', {'foo': 'other"thing'})
+ assert http.parse_options_header('something; foo="other\"thing"; meh=42') == \
+ ('something', {'foo': 'other"thing', 'meh': '42'})
+ assert http.parse_options_header('something; foo="other\"thing"; meh=42; bleh') == \
+ ('something', {'foo': 'other"thing', 'meh': '42', 'bleh': None})
+
+ def test_dump_options_header(self):
+ assert http.dump_options_header('foo', {'bar': 42}) == \
+ 'foo; bar=42'
+ assert http.dump_options_header('foo', {'bar': 42, 'fizz': None}) == \
+ 'foo; bar=42; fizz'
+
+ def test_dump_header(self):
+ assert http.dump_header([1, 2, 3]) == '1, 2, 3'
+ assert http.dump_header([1, 2, 3], allow_token=False) == '"1", "2", "3"'
+ assert http.dump_header({'foo': 'bar'}, allow_token=False) == 'foo="bar"'
+ assert http.dump_header({'foo': 'bar'}) == 'foo=bar'
+
+ def test_is_resource_modified(self):
+ env = create_environ()
+
+ # ignore POST
+ env['REQUEST_METHOD'] = 'POST'
+ assert not http.is_resource_modified(env, etag='testing')
+ env['REQUEST_METHOD'] = 'GET'
+
+ # etagify from data
+ self.assert_raises(TypeError, http.is_resource_modified, env,
+ data='42', etag='23')
+ env['HTTP_IF_NONE_MATCH'] = http.generate_etag('awesome')
+ assert not http.is_resource_modified(env, data='awesome')
+
+ env['HTTP_IF_MODIFIED_SINCE'] = http.http_date(datetime(2008, 1, 1, 12, 30))
+ assert not http.is_resource_modified(env,
+ last_modified=datetime(2008, 1, 1, 12, 00))
+ assert http.is_resource_modified(env,
+ last_modified=datetime(2008, 1, 1, 13, 00))
+
+ def test_date_formatting(self):
+ assert http.cookie_date(0) == 'Thu, 01-Jan-1970 00:00:00 GMT'
+ assert http.cookie_date(datetime(1970, 1, 1)) == 'Thu, 01-Jan-1970 00:00:00 GMT'
+ assert http.http_date(0) == 'Thu, 01 Jan 1970 00:00:00 GMT'
+ assert http.http_date(datetime(1970, 1, 1)) == 'Thu, 01 Jan 1970 00:00:00 GMT'
+
+ def test_cookies(self):
+ assert http.parse_cookie('dismiss-top=6; CP=null*; PHPSESSID=0a539d42abc001cd'
+ 'c762809248d4beed; a=42') == {
+ 'CP': u'null*',
+ 'PHPSESSID': u'0a539d42abc001cdc762809248d4beed',
+ 'a': u'42',
+ 'dismiss-top': u'6'
+ }
+ assert set(http.dump_cookie('foo', 'bar baz blub', 360, httponly=True,
+ sync_expires=False).split('; ')) == \
+ set(['HttpOnly', 'Max-Age=360', 'Path=/', 'foo="bar baz blub"'])
+ assert http.parse_cookie('fo234{=bar blub=Blah') == {'blub': 'Blah'}
+
+ def test_cookie_quoting(self):
+ val = http.dump_cookie("foo", "?foo")
+ assert val == 'foo="?foo"; Path=/'
+ assert http.parse_cookie(val) == {'foo': '?foo'}
+
+ assert http.parse_cookie(r'foo="foo\054bar"') == {'foo': 'foo,bar'}
+
+
+class RangeTestCase(WerkzeugTestCase):
+
+ def test_if_range_parsing(self):
+ rv = http.parse_if_range_header('"Test"')
+ assert rv.etag == 'Test'
+ assert rv.date is None
+ assert rv.to_header() == '"Test"'
+
+ # weak information is dropped
+ rv = http.parse_if_range_header('w/"Test"')
+ assert rv.etag == 'Test'
+ assert rv.date is None
+ assert rv.to_header() == '"Test"'
+
+ # broken etags are supported too
+ rv = http.parse_if_range_header('bullshit')
+ assert rv.etag == 'bullshit'
+ assert rv.date is None
+ assert rv.to_header() == '"bullshit"'
+
+ rv = http.parse_if_range_header('Thu, 01 Jan 1970 00:00:00 GMT')
+ assert rv.etag is None
+ assert rv.date == datetime(1970, 1, 1)
+ assert rv.to_header() == 'Thu, 01 Jan 1970 00:00:00 GMT'
+
+ for x in '', None:
+ rv = http.parse_if_range_header(x)
+ assert rv.etag is None
+ assert rv.date is None
+ assert rv.to_header() == ''
+
+ def test_range_parsing():
+ rv = http.parse_range_header('bytes=52')
+ assert rv is None
+
+ rv = http.parse_range_header('bytes=52-')
+ assert rv.units == 'bytes'
+ assert rv.ranges == [(52, None)]
+ assert rv.to_header() == 'bytes=52-'
+
+ rv = http.parse_range_header('bytes=52-99')
+ assert rv.units == 'bytes'
+ assert rv.ranges == [(52, 100)]
+ assert rv.to_header() == 'bytes=52-99'
+
+ rv = http.parse_range_header('bytes=52-99,-1000')
+ assert rv.units == 'bytes'
+ assert rv.ranges == [(52, 100), (-1000, None)]
+ assert rv.to_header() == 'bytes=52-99,-1000'
+
+ rv = http.parse_range_header('bytes = 1 - 100')
+ assert rv.units == 'bytes'
+ assert rv.ranges == [(1, 101)]
+ assert rv.to_header() == 'bytes=1-100'
+
+ rv = http.parse_range_header('AWesomes=0-999')
+ assert rv.units == 'awesomes'
+ assert rv.ranges == [(0, 1000)]
+ assert rv.to_header() == 'awesomes=0-999'
+
+ def test_content_range_parsing():
+ rv = http.parse_content_range_header('bytes 0-98/*')
+ assert rv.units == 'bytes'
+ assert rv.start == 0
+ assert rv.stop == 99
+ assert rv.length is None
+ assert rv.to_header() == 'bytes 0-98/*'
+
+ rv = http.parse_content_range_header('bytes 0-98/*asdfsa')
+ assert rv is None
+
+ rv = http.parse_content_range_header('bytes 0-99/100')
+ assert rv.to_header() == 'bytes 0-99/100'
+ rv.start = None
+ rv.stop = None
+ assert rv.units == 'bytes'
+ assert rv.to_header() == 'bytes */100'
+
+ rv = http.parse_content_range_header('bytes */100')
+ assert rv.start is None
+ assert rv.stop is None
+ assert rv.length == 100
+ assert rv.units == 'bytes'
+
+
+class RegressionTestCase(WerkzeugTestCase):
+
+ def test_best_match_works(self):
+ # was a bug in 0.6
+ rv = http.parse_accept_header('foo=,application/xml,application/xhtml+xml,'
+ 'text/html;q=0.9,text/plain;q=0.8,'
+ 'image/png,*/*;q=0.5',
+ datastructures.MIMEAccept).best_match(['foo/bar'])
+ self.assert_equal(rv, 'foo/bar')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(HTTPUtilityTestCase))
+ suite.addTest(unittest.makeSuite(RegressionTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/internal.py b/websdk/werkzeug/testsuite/internal.py
new file mode 100644
index 0000000..1de1b5d
--- /dev/null
+++ b/websdk/werkzeug/testsuite/internal.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.internal
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Internal tests.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from datetime import datetime
+from warnings import filterwarnings, resetwarnings
+
+from werkzeug.testsuite import WerkzeugTestCase
+from werkzeug.wrappers import Request, Response
+
+from werkzeug import _internal as internal
+from werkzeug.test import create_environ
+
+
+class InternalTestCase(WerkzeugTestCase):
+
+ def test_date_to_unix(self):
+ assert internal._date_to_unix(datetime(1970, 1, 1)) == 0
+ assert internal._date_to_unix(datetime(1970, 1, 1, 1, 0, 0)) == 3600
+ assert internal._date_to_unix(datetime(1970, 1, 1, 1, 1, 1)) == 3661
+ x = datetime(2010, 2, 15, 16, 15, 39)
+ assert internal._date_to_unix(x) == 1266250539
+
+ def test_easteregg(self):
+ req = Request.from_values('/?macgybarchakku')
+ resp = Response.force_type(internal._easteregg(None), req)
+ assert 'About Werkzeug' in resp.data
+ assert 'the Swiss Army knife of Python web development' in resp.data
+
+ def test_wrapper_internals(self):
+ req = Request.from_values(data={'foo': 'bar'}, method='POST')
+ req._load_form_data()
+ assert req.form.to_dict() == {'foo': 'bar'}
+
+ # second call does not break
+ req._load_form_data()
+ assert req.form.to_dict() == {'foo': 'bar'}
+
+ # check reprs
+ assert repr(req) == "<Request 'http://localhost/' [POST]>"
+ resp = Response()
+ assert repr(resp) == '<Response 0 bytes [200 OK]>'
+ resp.data = 'Hello World!'
+ assert repr(resp) == '<Response 12 bytes [200 OK]>'
+ resp.response = iter(['Test'])
+ assert repr(resp) == '<Response streamed [200 OK]>'
+
+ # unicode data does not set content length
+ response = Response([u'Hällo Wörld'])
+ headers = response.get_wsgi_headers(create_environ())
+ assert 'Content-Length' not in headers
+
+ response = Response(['Hällo Wörld'])
+ headers = response.get_wsgi_headers(create_environ())
+ assert 'Content-Length' in headers
+
+ # check for internal warnings
+ filterwarnings('error', category=Warning)
+ response = Response()
+ environ = create_environ()
+ response.response = 'What the...?'
+ self.assert_raises(Warning, lambda: list(response.iter_encoded()))
+ self.assert_raises(Warning, lambda: list(response.get_app_iter(environ)))
+ response.direct_passthrough = True
+ self.assert_raises(Warning, lambda: list(response.iter_encoded()))
+ self.assert_raises(Warning, lambda: list(response.get_app_iter(environ)))
+ resetwarnings()
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(InternalTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/local.py b/websdk/werkzeug/testsuite/local.py
new file mode 100644
index 0000000..236e753
--- /dev/null
+++ b/websdk/werkzeug/testsuite/local.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.local
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Local and local proxy tests.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import time
+import unittest
+from threading import Thread
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug import local
+
+
+class LocalTestCase(WerkzeugTestCase):
+
+ def test_basic_local(self):
+ l = local.Local()
+ l.foo = 0
+ values = []
+ def value_setter(idx):
+ time.sleep(0.01 * idx)
+ l.foo = idx
+ time.sleep(0.02)
+ values.append(l.foo)
+ threads = [Thread(target=value_setter, args=(x,))
+ for x in [1, 2, 3]]
+ for thread in threads:
+ thread.start()
+ time.sleep(0.2)
+ assert sorted(values) == [1, 2, 3]
+
+ def delfoo():
+ del l.foo
+ delfoo()
+ self.assert_raises(AttributeError, lambda: l.foo)
+ self.assert_raises(AttributeError, delfoo)
+
+ local.release_local(l)
+
+ def test_local_release(self):
+ loc = local.Local()
+ loc.foo = 42
+ local.release_local(loc)
+ assert not hasattr(loc, 'foo')
+
+ ls = local.LocalStack()
+ ls.push(42)
+ local.release_local(ls)
+ assert ls.top is None
+
+ def test_local_proxy(self):
+ foo = []
+ ls = local.LocalProxy(lambda: foo)
+ ls.append(42)
+ ls.append(23)
+ ls[1:] = [1, 2, 3]
+ assert foo == [42, 1, 2, 3]
+ assert repr(foo) == repr(ls)
+ assert foo[0] == 42
+ foo += [1]
+ assert list(foo) == [42, 1, 2, 3, 1]
+
+ def test_local_stack(self):
+ ident = local.get_ident()
+
+ ls = local.LocalStack()
+ assert ident not in ls._local.__storage__
+ assert ls.top is None
+ ls.push(42)
+ assert ident in ls._local.__storage__
+ assert ls.top == 42
+ ls.push(23)
+ assert ls.top == 23
+ ls.pop()
+ assert ls.top == 42
+ ls.pop()
+ assert ls.top is None
+ assert ls.pop() is None
+ assert ls.pop() is None
+
+ proxy = ls()
+ ls.push([1, 2])
+ assert proxy == [1, 2]
+ ls.push((1, 2))
+ assert proxy == (1, 2)
+ ls.pop()
+ ls.pop()
+ assert repr(proxy) == '<LocalProxy unbound>'
+
+ assert ident not in ls._local.__storage__
+
+ def test_local_proxies_with_callables(self):
+ foo = 42
+ ls = local.LocalProxy(lambda: foo)
+ assert ls == 42
+ foo = [23]
+ ls.append(42)
+ assert ls == [23, 42]
+ assert foo == [23, 42]
+
+ def test_custom_idents(self):
+ ident = 0
+ loc = local.Local()
+ stack = local.LocalStack()
+ mgr = local.LocalManager([loc, stack], ident_func=lambda: ident)
+
+ loc.foo = 42
+ stack.push({'foo': 42})
+ ident = 1
+ loc.foo = 23
+ stack.push({'foo': 23})
+ ident = 0
+ assert loc.foo == 42
+ assert stack.top['foo'] == 42
+ stack.pop()
+ assert stack.top is None
+ ident = 1
+ assert loc.foo == 23
+ assert stack.top['foo'] == 23
+ stack.pop()
+ assert stack.top is None
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(LocalTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/multipart/collect.py b/websdk/werkzeug/testsuite/multipart/collect.py
new file mode 100644
index 0000000..5fce5e9
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/collect.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+"""
+Hacky helper application to collect form data.
+"""
+from werkzeug.serving import run_simple
+from werkzeug.wrappers import Request, Response
+
+
+def copy_stream(request):
+ from os import mkdir
+ from time import time
+ folder = 'request-%d' % time()
+ mkdir(folder)
+ environ = request.environ
+ f = file(folder + '/request.txt', 'wb+')
+ f.write(environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])))
+ f.flush()
+ f.seek(0)
+ environ['wsgi.input'] = f
+ request.stat_folder = folder
+
+
+def stats(request):
+ copy_stream(request)
+ f1 = request.files['file1']
+ f2 = request.files['file2']
+ text = request.form['text']
+ f1.save(request.stat_folder + '/file1.bin')
+ f2.save(request.stat_folder + '/file2.bin')
+ file(request.stat_folder + '/text.txt', 'w').write(text.encode('utf-8'))
+ return Response('Done.')
+
+
+def upload_file(request):
+ return Response('''
+ <h1>Upload File</h1>
+ <form action="" method="post" enctype="multipart/form-data">
+ <input type="file" name="file1"><br>
+ <input type="file" name="file2"><br>
+ <textarea name="text"></textarea><br>
+ <input type="submit" value="Send">
+ </form>
+ ''', mimetype='text/html')
+
+
+def application(environ, start_responseonse):
+ request = Request(environ)
+ if request.method == 'POST':
+ response = stats(request)
+ else:
+ response = upload_file(request)
+ return response(environ, start_responseonse)
+
+
+if __name__ == '__main__':
+ run_simple('localhost', 5000, application, use_debugger=True)
diff --git a/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/file1.png b/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/file1.png
new file mode 100644
index 0000000..9b3422c
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/file1.png
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/file2.png b/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/file2.png
new file mode 100644
index 0000000..fb2efb8
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/file2.png
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/request.txt b/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/request.txt
new file mode 100644
index 0000000..721e04e
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/request.txt
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/text.txt b/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/text.txt
new file mode 100644
index 0000000..c87634d
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/firefox3-2png1txt/text.txt
@@ -0,0 +1 @@
+example text \ No newline at end of file
diff --git a/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/file1.png b/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/file1.png
new file mode 100644
index 0000000..89c8129
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/file1.png
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/file2.png b/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/file2.png
new file mode 100644
index 0000000..6332fef
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/file2.png
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/request.txt b/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/request.txt
new file mode 100644
index 0000000..489290b
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/request.txt
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/text.txt b/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/text.txt
new file mode 100644
index 0000000..3bf804d
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/firefox3-2pnglongtext/text.txt
@@ -0,0 +1,3 @@
+--long text
+--with boundary
+--lookalikes-- \ No newline at end of file
diff --git a/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/file1.png b/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/file1.png
new file mode 100644
index 0000000..9b3422c
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/file1.png
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/file2.png b/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/file2.png
new file mode 100644
index 0000000..fb2efb8
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/file2.png
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/request.txt b/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/request.txt
new file mode 100644
index 0000000..59fdeae
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/request.txt
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/text.txt b/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/text.txt
new file mode 100644
index 0000000..7c465b7
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/ie6-2png1txt/text.txt
@@ -0,0 +1 @@
+ie6 sucks :-/ \ No newline at end of file
diff --git a/websdk/werkzeug/testsuite/multipart/ie7_full_path_request.txt b/websdk/werkzeug/testsuite/multipart/ie7_full_path_request.txt
new file mode 100644
index 0000000..acc4e2e
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/ie7_full_path_request.txt
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/file1.png b/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/file1.png
new file mode 100644
index 0000000..7542db1
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/file1.png
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/file2.png b/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/file2.png
new file mode 100644
index 0000000..658c711
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/file2.png
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/request.txt b/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/request.txt
new file mode 100644
index 0000000..8f32591
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/request.txt
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/text.txt b/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/text.txt
new file mode 100644
index 0000000..ca01cb0
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/opera8-2png1txt/text.txt
@@ -0,0 +1 @@
+blafasel öäü \ No newline at end of file
diff --git a/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/file1.png b/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/file1.png
new file mode 100644
index 0000000..afca073
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/file1.png
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/file2.png b/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/file2.png
new file mode 100644
index 0000000..2a7da6e
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/file2.png
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/request.txt b/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/request.txt
new file mode 100644
index 0000000..b4ce0ee
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/request.txt
Binary files differ
diff --git a/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/text.txt b/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/text.txt
new file mode 100644
index 0000000..baa1300
--- /dev/null
+++ b/websdk/werkzeug/testsuite/multipart/webkit3-2png1txt/text.txt
@@ -0,0 +1 @@
+this is another text with ümläüts \ No newline at end of file
diff --git a/websdk/werkzeug/testsuite/res/test.txt b/websdk/werkzeug/testsuite/res/test.txt
new file mode 100644
index 0000000..a8efdcc
--- /dev/null
+++ b/websdk/werkzeug/testsuite/res/test.txt
@@ -0,0 +1 @@
+FOUND
diff --git a/websdk/werkzeug/testsuite/routing.py b/websdk/werkzeug/testsuite/routing.py
new file mode 100644
index 0000000..77b3cb6
--- /dev/null
+++ b/websdk/werkzeug/testsuite/routing.py
@@ -0,0 +1,610 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.routing
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Routing tests.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug import routing as r
+from werkzeug.wrappers import Response
+from werkzeug.datastructures import ImmutableDict
+from werkzeug.test import create_environ
+
+
+class RoutingTestCase(WerkzeugTestCase):
+
+ def test_basic_routing(self):
+ map = r.Map([
+ r.Rule('/', endpoint='index'),
+ r.Rule('/foo', endpoint='foo'),
+ r.Rule('/bar/', endpoint='bar')
+ ])
+ adapter = map.bind('example.org', '/')
+ assert adapter.match('/') == ('index', {})
+ assert adapter.match('/foo') == ('foo', {})
+ assert adapter.match('/bar/') == ('bar', {})
+ self.assert_raises(r.RequestRedirect, lambda: adapter.match('/bar'))
+ self.assert_raises(r.NotFound, lambda: adapter.match('/blub'))
+
+ adapter = map.bind('example.org', '/test')
+ try:
+ adapter.match('/bar')
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://example.org/test/bar/'
+ else:
+ self.fail('Expected request redirect')
+
+ adapter = map.bind('example.org', '/')
+ try:
+ adapter.match('/bar')
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://example.org/bar/'
+ else:
+ self.fail('Expected request redirect')
+
+ adapter = map.bind('example.org', '/')
+ try:
+ adapter.match('/bar', query_args={'aha': 'muhaha'})
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://example.org/bar/?aha=muhaha'
+ else:
+ self.fail('Expected request redirect')
+
+ adapter = map.bind('example.org', '/')
+ try:
+ adapter.match('/bar', query_args='aha=muhaha')
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://example.org/bar/?aha=muhaha'
+ else:
+ self.fail('Expected request redirect')
+
+ adapter = map.bind_to_environ(create_environ('/bar?foo=bar',
+ 'http://example.org/'))
+ try:
+ adapter.match()
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://example.org/bar/?foo=bar'
+ else:
+ self.fail('Expected request redirect')
+
+ def test_environ_defaults(self):
+ environ = create_environ("/foo")
+ self.assert_equal(environ["PATH_INFO"], '/foo')
+ m = r.Map([r.Rule("/foo", endpoint="foo"), r.Rule("/bar", endpoint="bar")])
+ a = m.bind_to_environ(environ)
+ self.assert_equal(a.match("/foo"), ('foo', {}))
+ self.assert_equal(a.match(), ('foo', {}))
+ self.assert_equal(a.match("/bar"), ('bar', {}))
+ self.assert_raises(r.NotFound, a.match, "/bars")
+
+ def test_basic_building(self):
+ map = r.Map([
+ r.Rule('/', endpoint='index'),
+ r.Rule('/foo', endpoint='foo'),
+ r.Rule('/bar/<baz>', endpoint='bar'),
+ r.Rule('/bar/<int:bazi>', endpoint='bari'),
+ r.Rule('/bar/<float:bazf>', endpoint='barf'),
+ r.Rule('/bar/<path:bazp>', endpoint='barp'),
+ r.Rule('/hehe', endpoint='blah', subdomain='blah')
+ ])
+ adapter = map.bind('example.org', '/', subdomain='blah')
+
+ assert adapter.build('index', {}) == 'http://example.org/'
+ assert adapter.build('foo', {}) == 'http://example.org/foo'
+ assert adapter.build('bar', {'baz': 'blub'}) == 'http://example.org/bar/blub'
+ assert adapter.build('bari', {'bazi': 50}) == 'http://example.org/bar/50'
+ assert adapter.build('barf', {'bazf': 0.815}) == 'http://example.org/bar/0.815'
+ assert adapter.build('barp', {'bazp': 'la/di'}) == 'http://example.org/bar/la/di'
+ assert adapter.build('blah', {}) == '/hehe'
+ self.assert_raises(r.BuildError, lambda: adapter.build('urks'))
+
+ adapter = map.bind('example.org', '/test', subdomain='blah')
+ assert adapter.build('index', {}) == 'http://example.org/test/'
+ assert adapter.build('foo', {}) == 'http://example.org/test/foo'
+ assert adapter.build('bar', {'baz': 'blub'}) == 'http://example.org/test/bar/blub'
+ assert adapter.build('bari', {'bazi': 50}) == 'http://example.org/test/bar/50'
+ assert adapter.build('barf', {'bazf': 0.815}) == 'http://example.org/test/bar/0.815'
+ assert adapter.build('barp', {'bazp': 'la/di'}) == 'http://example.org/test/bar/la/di'
+ assert adapter.build('blah', {}) == '/test/hehe'
+
+ def test_defaults(self):
+ map = r.Map([
+ r.Rule('/foo/', defaults={'page': 1}, endpoint='foo'),
+ r.Rule('/foo/<int:page>', endpoint='foo')
+ ])
+ adapter = map.bind('example.org', '/')
+
+ assert adapter.match('/foo/') == ('foo', {'page': 1})
+ self.assert_raises(r.RequestRedirect, lambda: adapter.match('/foo/1'))
+ assert adapter.match('/foo/2') == ('foo', {'page': 2})
+ assert adapter.build('foo', {}) == '/foo/'
+ assert adapter.build('foo', {'page': 1}) == '/foo/'
+ assert adapter.build('foo', {'page': 2}) == '/foo/2'
+
+ def test_greedy(self):
+ map = r.Map([
+ r.Rule('/foo', endpoint='foo'),
+ r.Rule('/<path:bar>', endpoint='bar'),
+ r.Rule('/<path:bar>/<path:blub>', endpoint='bar')
+ ])
+ adapter = map.bind('example.org', '/')
+
+ assert adapter.match('/foo') == ('foo', {})
+ assert adapter.match('/blub') == ('bar', {'bar': 'blub'})
+ assert adapter.match('/he/he') == ('bar', {'bar': 'he', 'blub': 'he'})
+
+ assert adapter.build('foo', {}) == '/foo'
+ assert adapter.build('bar', {'bar': 'blub'}) == '/blub'
+ assert adapter.build('bar', {'bar': 'blub', 'blub': 'bar'}) == '/blub/bar'
+
+ def test_path(self):
+ map = r.Map([
+ r.Rule('/', defaults={'name': 'FrontPage'}, endpoint='page'),
+ r.Rule('/Special', endpoint='special'),
+ r.Rule('/<int:year>', endpoint='year'),
+ r.Rule('/<path:name>', endpoint='page'),
+ r.Rule('/<path:name>/edit', endpoint='editpage'),
+ r.Rule('/<path:name>/silly/<path:name2>', endpoint='sillypage'),
+ r.Rule('/<path:name>/silly/<path:name2>/edit', endpoint='editsillypage'),
+ r.Rule('/Talk:<path:name>', endpoint='talk'),
+ r.Rule('/User:<username>', endpoint='user'),
+ r.Rule('/User:<username>/<path:name>', endpoint='userpage'),
+ r.Rule('/Files/<path:file>', endpoint='files'),
+ ])
+ adapter = map.bind('example.org', '/')
+
+ assert adapter.match('/') == ('page', {'name':'FrontPage'})
+ self.assert_raises(r.RequestRedirect, lambda: adapter.match('/FrontPage'))
+ assert adapter.match('/Special') == ('special', {})
+ assert adapter.match('/2007') == ('year', {'year':2007})
+ assert adapter.match('/Some/Page') == ('page', {'name':'Some/Page'})
+ assert adapter.match('/Some/Page/edit') == ('editpage', {'name':'Some/Page'})
+ assert adapter.match('/Foo/silly/bar') == ('sillypage', {'name':'Foo', 'name2':'bar'})
+ assert adapter.match('/Foo/silly/bar/edit') == ('editsillypage', {'name':'Foo', 'name2':'bar'})
+ assert adapter.match('/Talk:Foo/Bar') == ('talk', {'name':'Foo/Bar'})
+ assert adapter.match('/User:thomas') == ('user', {'username':'thomas'})
+ assert adapter.match('/User:thomas/projects/werkzeug') == \
+ ('userpage', {'username':'thomas', 'name':'projects/werkzeug'})
+ assert adapter.match('/Files/downloads/werkzeug/0.2.zip') == \
+ ('files', {'file':'downloads/werkzeug/0.2.zip'})
+
+ def test_dispatch(self):
+ env = create_environ('/')
+ map = r.Map([
+ r.Rule('/', endpoint='root'),
+ r.Rule('/foo/', endpoint='foo')
+ ])
+ adapter = map.bind_to_environ(env)
+
+ raise_this = None
+ def view_func(endpoint, values):
+ if raise_this is not None:
+ raise raise_this
+ return Response(repr((endpoint, values)))
+ dispatch = lambda p, q=False: Response.force_type(adapter.dispatch(view_func, p,
+ catch_http_exceptions=q), env)
+
+ assert dispatch('/').data == "('root', {})"
+ assert dispatch('/foo').status_code == 301
+ raise_this = r.NotFound()
+ self.assert_raises(r.NotFound, lambda: dispatch('/bar'))
+ assert dispatch('/bar', True).status_code == 404
+
+ def test_http_host_before_server_name(self):
+ env = {
+ 'HTTP_HOST': 'wiki.example.com',
+ 'SERVER_NAME': 'web0.example.com',
+ 'SERVER_PORT': '80',
+ 'SCRIPT_NAME': '',
+ 'PATH_INFO': '',
+ 'REQUEST_METHOD': 'GET',
+ 'wsgi.url_scheme': 'http'
+ }
+ map = r.Map([r.Rule('/', endpoint='index', subdomain='wiki')])
+ adapter = map.bind_to_environ(env, server_name='example.com')
+ assert adapter.match('/') == ('index', {})
+ assert adapter.build('index', force_external=True) == 'http://wiki.example.com/'
+ assert adapter.build('index') == '/'
+
+ env['HTTP_HOST'] = 'admin.example.com'
+ adapter = map.bind_to_environ(env, server_name='example.com')
+ assert adapter.build('index') == 'http://wiki.example.com/'
+
+ def test_adapter_url_parameter_sorting(self):
+ map = r.Map([r.Rule('/', endpoint='index')], sort_parameters=True,
+ sort_key=lambda x: x[1])
+ adapter = map.bind('localhost', '/')
+ assert adapter.build('index', {'x': 20, 'y': 10, 'z': 30},
+ force_external=True) == 'http://localhost/?y=10&x=20&z=30'
+
+ def test_request_direct_charset_bug(self):
+ map = r.Map([r.Rule(u'/öäü/')])
+ adapter = map.bind('localhost', '/')
+ try:
+ adapter.match(u'/öäü')
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://localhost/%C3%B6%C3%A4%C3%BC/'
+ else:
+ self.fail('expected request redirect exception')
+
+ def test_request_redirect_default(self):
+ map = r.Map([r.Rule(u'/foo', defaults={'bar': 42}),
+ r.Rule(u'/foo/<int:bar>')])
+ adapter = map.bind('localhost', '/')
+ try:
+ adapter.match(u'/foo/42')
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://localhost/foo'
+ else:
+ self.fail('expected request redirect exception')
+
+ def test_request_redirect_default_subdomain(self):
+ map = r.Map([r.Rule(u'/foo', defaults={'bar': 42}, subdomain='test'),
+ r.Rule(u'/foo/<int:bar>', subdomain='other')])
+ adapter = map.bind('localhost', '/', subdomain='other')
+ try:
+ adapter.match(u'/foo/42')
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://test.localhost/foo'
+ else:
+ self.fail('expected request redirect exception')
+
+ def test_adapter_match_return_rule(self):
+ rule = r.Rule('/foo/', endpoint='foo')
+ map = r.Map([rule])
+ adapter = map.bind('localhost', '/')
+ assert adapter.match('/foo/', return_rule=True) == (rule, {})
+
+ def test_server_name_interpolation(self):
+ server_name = 'example.invalid'
+ map = r.Map([r.Rule('/', endpoint='index'),
+ r.Rule('/', endpoint='alt', subdomain='alt')])
+
+ env = create_environ('/', 'http://%s/' % server_name)
+ adapter = map.bind_to_environ(env, server_name=server_name)
+ assert adapter.match() == ('index', {})
+
+ env = create_environ('/', 'http://alt.%s/' % server_name)
+ adapter = map.bind_to_environ(env, server_name=server_name)
+ assert adapter.match() == ('alt', {})
+
+ env = create_environ('/', 'http://%s/' % server_name)
+ adapter = map.bind_to_environ(env, server_name='foo')
+ assert adapter.subdomain == '<invalid>'
+
+ def test_rule_emptying(self):
+ rule = r.Rule('/foo', {'meh': 'muh'}, 'x', ['POST'],
+ False, 'x', True, None)
+ rule2 = rule.empty()
+ assert rule.__dict__ == rule2.__dict__
+ rule.methods.add('GET')
+ assert rule.__dict__ != rule2.__dict__
+ rule.methods.discard('GET')
+ rule.defaults['meh'] = 'aha'
+ assert rule.__dict__ != rule2.__dict__
+
+ def test_rule_templates(self):
+ testcase = r.RuleTemplate(
+ [ r.Submount('/test/$app',
+ [ r.Rule('/foo/', endpoint='handle_foo')
+ , r.Rule('/bar/', endpoint='handle_bar')
+ , r.Rule('/baz/', endpoint='handle_baz')
+ ]),
+ r.EndpointPrefix('${app}',
+ [ r.Rule('/${app}-blah', endpoint='bar')
+ , r.Rule('/${app}-meh', endpoint='baz')
+ ]),
+ r.Subdomain('$app',
+ [ r.Rule('/blah', endpoint='x_bar')
+ , r.Rule('/meh', endpoint='x_baz')
+ ])
+ ])
+
+ url_map = r.Map(
+ [ testcase(app='test1')
+ , testcase(app='test2')
+ , testcase(app='test3')
+ , testcase(app='test4')
+ ])
+
+ out = sorted([(x.rule, x.subdomain, x.endpoint)
+ for x in url_map.iter_rules()])
+
+ assert out == ([
+ ('/blah', 'test1', 'x_bar'),
+ ('/blah', 'test2', 'x_bar'),
+ ('/blah', 'test3', 'x_bar'),
+ ('/blah', 'test4', 'x_bar'),
+ ('/meh', 'test1', 'x_baz'),
+ ('/meh', 'test2', 'x_baz'),
+ ('/meh', 'test3', 'x_baz'),
+ ('/meh', 'test4', 'x_baz'),
+ ('/test/test1/bar/', '', 'handle_bar'),
+ ('/test/test1/baz/', '', 'handle_baz'),
+ ('/test/test1/foo/', '', 'handle_foo'),
+ ('/test/test2/bar/', '', 'handle_bar'),
+ ('/test/test2/baz/', '', 'handle_baz'),
+ ('/test/test2/foo/', '', 'handle_foo'),
+ ('/test/test3/bar/', '', 'handle_bar'),
+ ('/test/test3/baz/', '', 'handle_baz'),
+ ('/test/test3/foo/', '', 'handle_foo'),
+ ('/test/test4/bar/', '', 'handle_bar'),
+ ('/test/test4/baz/', '', 'handle_baz'),
+ ('/test/test4/foo/', '', 'handle_foo'),
+ ('/test1-blah', '', 'test1bar'),
+ ('/test1-meh', '', 'test1baz'),
+ ('/test2-blah', '', 'test2bar'),
+ ('/test2-meh', '', 'test2baz'),
+ ('/test3-blah', '', 'test3bar'),
+ ('/test3-meh', '', 'test3baz'),
+ ('/test4-blah', '', 'test4bar'),
+ ('/test4-meh', '', 'test4baz')
+ ])
+
+ def test_complex_routing_rules(self):
+ m = r.Map([
+ r.Rule('/', endpoint='index'),
+ r.Rule('/<int:blub>', endpoint='an_int'),
+ r.Rule('/<blub>', endpoint='a_string'),
+ r.Rule('/foo/', endpoint='nested'),
+ r.Rule('/foobar/', endpoint='nestedbar'),
+ r.Rule('/foo/<path:testing>/', endpoint='nested_show'),
+ r.Rule('/foo/<path:testing>/edit', endpoint='nested_edit'),
+ r.Rule('/users/', endpoint='users', defaults={'page': 1}),
+ r.Rule('/users/page/<int:page>', endpoint='users'),
+ r.Rule('/foox', endpoint='foox'),
+ r.Rule('/<path:bar>/<path:blub>', endpoint='barx_path_path')
+ ])
+ a = m.bind('example.com')
+
+ assert a.match('/') == ('index', {})
+ assert a.match('/42') == ('an_int', {'blub': 42})
+ assert a.match('/blub') == ('a_string', {'blub': 'blub'})
+ assert a.match('/foo/') == ('nested', {})
+ assert a.match('/foobar/') == ('nestedbar', {})
+ assert a.match('/foo/1/2/3/') == ('nested_show', {'testing': '1/2/3'})
+ assert a.match('/foo/1/2/3/edit') == ('nested_edit', {'testing': '1/2/3'})
+ assert a.match('/users/') == ('users', {'page': 1})
+ assert a.match('/users/page/2') == ('users', {'page': 2})
+ assert a.match('/foox') == ('foox', {})
+ assert a.match('/1/2/3') == ('barx_path_path', {'bar': '1', 'blub': '2/3'})
+
+ assert a.build('index') == '/'
+ assert a.build('an_int', {'blub': 42}) == '/42'
+ assert a.build('a_string', {'blub': 'test'}) == '/test'
+ assert a.build('nested') == '/foo/'
+ assert a.build('nestedbar') == '/foobar/'
+ assert a.build('nested_show', {'testing': '1/2/3'}) == '/foo/1/2/3/'
+ assert a.build('nested_edit', {'testing': '1/2/3'}) == '/foo/1/2/3/edit'
+ assert a.build('users', {'page': 1}) == '/users/'
+ assert a.build('users', {'page': 2}) == '/users/page/2'
+ assert a.build('foox') == '/foox'
+ assert a.build('barx_path_path', {'bar': '1', 'blub': '2/3'}) == '/1/2/3'
+
+ def test_default_converters(self):
+ class MyMap(r.Map):
+ default_converters = r.Map.default_converters.copy()
+ default_converters['foo'] = r.UnicodeConverter
+ assert isinstance(r.Map.default_converters, ImmutableDict)
+ m = MyMap([
+ r.Rule('/a/<foo:a>', endpoint='a'),
+ r.Rule('/b/<foo:b>', endpoint='b'),
+ r.Rule('/c/<c>', endpoint='c')
+ ], converters={'bar': r.UnicodeConverter})
+ a = m.bind('example.org', '/')
+ assert a.match('/a/1') == ('a', {'a': '1'})
+ assert a.match('/b/2') == ('b', {'b': '2'})
+ assert a.match('/c/3') == ('c', {'c': '3'})
+ assert 'foo' not in r.Map.default_converters
+
+ def test_build_append_unknown(self):
+ map = r.Map([
+ r.Rule('/bar/<float:bazf>', endpoint='barf')
+ ])
+ adapter = map.bind('example.org', '/', subdomain='blah')
+ assert adapter.build('barf', {'bazf': 0.815, 'bif' : 1.0}) == \
+ 'http://example.org/bar/0.815?bif=1.0'
+ assert adapter.build('barf', {'bazf': 0.815, 'bif' : 1.0},
+ append_unknown=False) == 'http://example.org/bar/0.815'
+
+ def test_method_fallback(self):
+ map = r.Map([
+ r.Rule('/', endpoint='index', methods=['GET']),
+ r.Rule('/<name>', endpoint='hello_name', methods=['GET']),
+ r.Rule('/select', endpoint='hello_select', methods=['POST']),
+ r.Rule('/search_get', endpoint='search', methods=['GET']),
+ r.Rule('/search_post', endpoint='search', methods=['POST'])
+ ])
+ adapter = map.bind('example.com')
+ assert adapter.build('index') == '/'
+ assert adapter.build('index', method='GET') == '/'
+ assert adapter.build('hello_name', {'name': 'foo'}) == '/foo'
+ assert adapter.build('hello_select') == '/select'
+ assert adapter.build('hello_select', method='POST') == '/select'
+ assert adapter.build('search') == '/search_get'
+ assert adapter.build('search', method='GET') == '/search_get'
+ assert adapter.build('search', method='POST') == '/search_post'
+
+ def test_implicit_head(self):
+ url_map = r.Map([
+ r.Rule('/get', methods=['GET'], endpoint='a'),
+ r.Rule('/post', methods=['POST'], endpoint='b')
+ ])
+ adapter = url_map.bind('example.org')
+ assert adapter.match('/get', method='HEAD') == ('a', {})
+ self.assert_raises(r.MethodNotAllowed, adapter.match,
+ '/post', method='HEAD')
+
+ def test_protocol_joining_bug(self):
+ m = r.Map([r.Rule('/<foo>', endpoint='x')])
+ a = m.bind('example.org')
+ assert a.build('x', {'foo': 'x:y'}) == '/x:y'
+ assert a.build('x', {'foo': 'x:y'}, force_external=True) == \
+ 'http://example.org/x:y'
+
+ def test_allowed_methods_querying(self):
+ m = r.Map([r.Rule('/<foo>', methods=['GET', 'HEAD']),
+ r.Rule('/foo', methods=['POST'])])
+ a = m.bind('example.org')
+ assert sorted(a.allowed_methods('/foo')) == ['GET', 'HEAD', 'POST']
+
+ def test_external_building_with_port(self):
+ map = r.Map([
+ r.Rule('/', endpoint='index'),
+ ])
+ adapter = map.bind('example.org:5000', '/')
+ built_url = adapter.build('index', {}, force_external=True)
+ assert built_url == 'http://example.org:5000/', built_url
+
+ def test_external_building_with_port_bind_to_environ(self):
+ map = r.Map([
+ r.Rule('/', endpoint='index'),
+ ])
+ adapter = map.bind_to_environ(
+ create_environ('/', 'http://example.org:5000/'),
+ server_name="example.org:5000"
+ )
+ built_url = adapter.build('index', {}, force_external=True)
+ assert built_url == 'http://example.org:5000/', built_url
+
+ def test_external_building_with_port_bind_to_environ_wrong_servername(self):
+ map = r.Map([
+ r.Rule('/', endpoint='index'),
+ ])
+ environ = create_environ('/', 'http://example.org:5000/')
+ adapter = map.bind_to_environ(environ, server_name="example.org")
+ assert adapter.subdomain == '<invalid>'
+
+ def test_converter_parser(self):
+ args, kwargs = r.parse_converter_args(u'test, a=1, b=3.0')
+
+ assert args == ('test',)
+ assert kwargs == {'a': 1, 'b': 3.0 }
+
+ args, kwargs = r.parse_converter_args('')
+ assert not args and not kwargs
+
+ args, kwargs = r.parse_converter_args('a, b, c,')
+ assert args == ('a', 'b', 'c')
+ assert not kwargs
+
+ args, kwargs = r.parse_converter_args('True, False, None')
+ assert args == (True, False, None)
+
+ args, kwargs = r.parse_converter_args('"foo", u"bar"')
+ assert args == ('foo', 'bar')
+
+ def test_alias_redirects(self):
+ m = r.Map([
+ r.Rule('/', endpoint='index'),
+ r.Rule('/index.html', endpoint='index', alias=True),
+ r.Rule('/users/', defaults={'page': 1}, endpoint='users'),
+ r.Rule('/users/index.html', defaults={'page': 1}, alias=True,
+ endpoint='users'),
+ r.Rule('/users/page/<int:page>', endpoint='users'),
+ r.Rule('/users/page-<int:page>.html', alias=True, endpoint='users'),
+ ])
+ a = m.bind('example.com')
+
+ def ensure_redirect(path, new_url, args=None):
+ try:
+ a.match(path, query_args=args)
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://example.com' + new_url
+ else:
+ assert False, 'expected redirect'
+
+ ensure_redirect('/index.html', '/')
+ ensure_redirect('/users/index.html', '/users/')
+ ensure_redirect('/users/page-2.html', '/users/page/2')
+ ensure_redirect('/users/page-1.html', '/users/')
+ ensure_redirect('/users/page-1.html', '/users/?foo=bar', {'foo': 'bar'})
+
+ assert a.build('index') == '/'
+ assert a.build('users', {'page': 1}) == '/users/'
+ assert a.build('users', {'page': 2}) == '/users/page/2'
+
+ def test_double_defaults(self):
+ for prefix in '', '/aaa':
+ m = r.Map([
+ r.Rule(prefix + '/', defaults={'foo': 1, 'bar': False}, endpoint='x'),
+ r.Rule(prefix + '/<int:foo>', defaults={'bar': False}, endpoint='x'),
+ r.Rule(prefix + '/bar/', defaults={'foo': 1, 'bar': True}, endpoint='x'),
+ r.Rule(prefix + '/bar/<int:foo>', defaults={'bar': True}, endpoint='x')
+ ])
+ a = m.bind('example.com')
+
+ assert a.match(prefix + '/') == ('x', {'foo': 1, 'bar': False})
+ assert a.match(prefix + '/2') == ('x', {'foo': 2, 'bar': False})
+ assert a.match(prefix + '/bar/') == ('x', {'foo': 1, 'bar': True})
+ assert a.match(prefix + '/bar/2') == ('x', {'foo': 2, 'bar': True})
+
+ assert a.build('x', {'foo': 1, 'bar': False}) == prefix + '/'
+ assert a.build('x', {'foo': 2, 'bar': False}) == prefix + '/2'
+ assert a.build('x', {'bar': False}) == prefix + '/'
+ assert a.build('x', {'foo': 1, 'bar': True}) == prefix + '/bar/'
+ assert a.build('x', {'foo': 2, 'bar': True}) == prefix + '/bar/2'
+ assert a.build('x', {'bar': True}) == prefix + '/bar/'
+
+ def test_host_matching(self):
+ m = r.Map([
+ r.Rule('/', endpoint='index', host='www.<domain>'),
+ r.Rule('/', endpoint='files', host='files.<domain>'),
+ r.Rule('/foo/', defaults={'page': 1}, host='www.<domain>', endpoint='x'),
+ r.Rule('/<int:page>', host='files.<domain>', endpoint='x')
+ ], host_matching=True)
+
+ a = m.bind('www.example.com')
+ assert a.match('/') == ('index', {'domain': 'example.com'})
+ assert a.match('/foo/') == ('x', {'domain': 'example.com', 'page': 1})
+ try:
+ a.match('/foo')
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://www.example.com/foo/'
+ else:
+ assert False, 'expected redirect'
+
+ a = m.bind('files.example.com')
+ assert a.match('/') == ('files', {'domain': 'example.com'})
+ assert a.match('/2') == ('x', {'domain': 'example.com', 'page': 2})
+ try:
+ a.match('/1')
+ except r.RequestRedirect, e:
+ assert e.new_url == 'http://www.example.com/foo/'
+ else:
+ assert False, 'expected redirect'
+
+ def test_server_name_casing(self):
+ m = r.Map([
+ r.Rule('/', endpoint='index', subdomain='foo')
+ ])
+
+ env = create_environ()
+ env['SERVER_NAME'] = env['HTTP_HOST'] = 'FOO.EXAMPLE.COM'
+ a = m.bind_to_environ(env, server_name='example.com')
+ assert a.match('/') == ('index', {})
+
+ env = create_environ()
+ env['SERVER_NAME'] = '127.0.0.1'
+ env['SERVER_PORT'] = '5000'
+ del env['HTTP_HOST']
+ a = m.bind_to_environ(env, server_name='example.com')
+ try:
+ a.match()
+ except r.NotFound:
+ pass
+ else:
+ assert False, 'Expected not found exception'
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(RoutingTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/security.py b/websdk/werkzeug/testsuite/security.py
new file mode 100644
index 0000000..bdd0b34
--- /dev/null
+++ b/websdk/werkzeug/testsuite/security.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.security
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the security helpers.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import os
+import unittest
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug.security import check_password_hash, generate_password_hash, \
+ safe_join
+
+
+class SecurityTestCase(WerkzeugTestCase):
+
+ def test_password_hashing(self):
+ """Test the password hashing and password hash checking"""
+ hash1 = generate_password_hash('default')
+ hash2 = generate_password_hash(u'default', method='sha1')
+ assert hash1 != hash2
+ assert check_password_hash(hash1, 'default')
+ assert check_password_hash(hash2, 'default')
+ assert hash1.startswith('sha1$')
+ assert hash2.startswith('sha1$')
+
+ fakehash = generate_password_hash('default', method='plain')
+ assert fakehash == 'plain$$default'
+ assert check_password_hash(fakehash, 'default')
+
+ mhash = generate_password_hash(u'default', method='md5')
+ assert mhash.startswith('md5$')
+ assert check_password_hash(mhash, 'default')
+
+ legacy = 'md5$$c21f969b5f03d33d43e04f8f136e7682'
+ assert check_password_hash(legacy, 'default')
+
+ legacy = u'md5$$c21f969b5f03d33d43e04f8f136e7682'
+ assert check_password_hash(legacy, 'default')
+
+ def test_safe_join(self):
+ """Test the safe joining helper"""
+ assert safe_join('foo', 'bar/baz') == os.path.join('foo', 'bar/baz')
+ assert safe_join('foo', '../bar/baz') is None
+ if os.name == 'nt':
+ assert safe_join('foo', 'foo\\bar') is None
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(SecurityTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/serving.py b/websdk/werkzeug/testsuite/serving.py
new file mode 100644
index 0000000..e26271d
--- /dev/null
+++ b/websdk/werkzeug/testsuite/serving.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.serving
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Added serving tests.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import sys
+import time
+import urllib
+import unittest
+from functools import update_wrapper
+from StringIO import StringIO
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug import __version__ as version, serving
+from werkzeug.testapp import test_app
+from threading import Thread
+
+
+real_make_server = serving.make_server
+
+
+def silencestderr(f):
+ def new_func(*args, **kwargs):
+ old_stderr = sys.stderr
+ sys.stderr = StringIO()
+ try:
+ return f(*args, **kwargs)
+ finally:
+ sys.stderr = old_stderr
+ return update_wrapper(new_func, f)
+
+
+def run_dev_server(application):
+ servers = []
+ def tracking_make_server(*args, **kwargs):
+ srv = real_make_server(*args, **kwargs)
+ servers.append(srv)
+ return srv
+ serving.make_server = tracking_make_server
+ try:
+ t = Thread(target=serving.run_simple, args=('localhost', 0, application))
+ t.setDaemon(True)
+ t.start()
+ time.sleep(0.25)
+ finally:
+ serving.make_server = real_make_server
+ if not servers:
+ return None, None
+ server ,= servers
+ ip, port = server.socket.getsockname()[:2]
+ if ':' in ip:
+ ip = '[%s]' % ip
+ return server, '%s:%d' % (ip, port)
+
+
+class ServingTestCase(WerkzeugTestCase):
+
+ @silencestderr
+ def test_serving(self):
+ server, addr = run_dev_server(test_app)
+ rv = urllib.urlopen('http://%s/?foo=bar&baz=blah' % addr).read()
+ assert 'WSGI Information' in rv
+ assert 'foo=bar&amp;baz=blah' in rv
+ assert ('Werkzeug/%s' % version) in rv
+
+ @silencestderr
+ def test_broken_app(self):
+ def broken_app(environ, start_response):
+ 1/0
+ server, addr = run_dev_server(broken_app)
+ rv = urllib.urlopen('http://%s/?foo=bar&baz=blah' % addr).read()
+ assert 'Internal Server Error' in rv
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ServingTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/test.py b/websdk/werkzeug/testsuite/test.py
new file mode 100644
index 0000000..795de90
--- /dev/null
+++ b/websdk/werkzeug/testsuite/test.py
@@ -0,0 +1,376 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.test
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the testing tools.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import sys
+import unittest
+from cStringIO import StringIO, OutputType
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug.wrappers import Request, Response, BaseResponse
+from werkzeug.test import Client, EnvironBuilder, create_environ, \
+ ClientRedirectError, stream_encode_multipart, run_wsgi_app
+from werkzeug.utils import redirect
+from werkzeug.formparser import parse_form_data
+from werkzeug.datastructures import MultiDict
+
+
+def cookie_app(environ, start_response):
+ """A WSGI application which sets a cookie, and returns as a ersponse any
+ cookie which exists.
+ """
+ response = Response(environ.get('HTTP_COOKIE', 'No Cookie'),
+ mimetype='text/plain')
+ response.set_cookie('test', 'test')
+ return response(environ, start_response)
+
+
+def redirect_loop_app(environ, start_response):
+ response = redirect('http://localhost/some/redirect/')
+ return response(environ, start_response)
+
+
+def redirect_with_get_app(environ, start_response):
+ req = Request(environ)
+ if req.url not in ('http://localhost/',
+ 'http://localhost/first/request',
+ 'http://localhost/some/redirect/'):
+ assert False, 'redirect_demo_app() did not expect URL "%s"' % req.url
+ if '/some/redirect' not in req.url:
+ response = redirect('http://localhost/some/redirect/')
+ else:
+ response = Response('current url: %s' % req.url)
+ return response(environ, start_response)
+
+
+def redirect_with_post_app(environ, start_response):
+ req = Request(environ)
+ if req.url == 'http://localhost/some/redirect/':
+ assert req.method == 'GET', 'request should be GET'
+ assert not req.form, 'request should not have data'
+ response = Response('current url: %s' % req.url)
+ else:
+ response = redirect('http://localhost/some/redirect/')
+ return response(environ, start_response)
+
+
+def external_redirect_demo_app(environ, start_response):
+ response = redirect('http://example.com/')
+ return response(environ, start_response)
+
+
+def external_subdomain_redirect_demo_app(environ, start_response):
+ if 'test.example.com' in environ['HTTP_HOST']:
+ response = Response('redirected successfully to subdomain')
+ else:
+ response = redirect('http://test.example.com/login')
+ return response(environ, start_response)
+
+
+def multi_value_post_app(environ, start_response):
+ req = Request(environ)
+ assert req.form['field'] == 'val1', req.form['field']
+ assert req.form.getlist('field') == ['val1', 'val2'], req.form.getlist('field')
+ response = Response('ok')
+ return response(environ, start_response)
+
+
+class TestTestCase(WerkzeugTestCase):
+
+ def test_cookie_forging(self):
+ c = Client(cookie_app)
+ c.set_cookie('localhost', 'foo', 'bar')
+ appiter, code, headers = c.open()
+ assert list(appiter) == ['foo=bar']
+
+ def test_set_cookie_app(self):
+ c = Client(cookie_app)
+ appiter, code, headers = c.open()
+ assert 'Set-Cookie' in dict(headers)
+
+ def test_cookiejar_stores_cookie(self):
+ c = Client(cookie_app)
+ appiter, code, headers = c.open()
+ assert 'test' in c.cookie_jar._cookies['localhost.local']['/']
+
+ def test_no_initial_cookie(self):
+ c = Client(cookie_app)
+ appiter, code, headers = c.open()
+ assert ''.join(appiter) == 'No Cookie'
+
+ def test_resent_cookie(self):
+ c = Client(cookie_app)
+ c.open()
+ appiter, code, headers = c.open()
+ assert ''.join(appiter) == 'test=test'
+
+ def test_disable_cookies(self):
+ c = Client(cookie_app, use_cookies=False)
+ c.open()
+ appiter, code, headers = c.open()
+ assert ''.join(appiter) == 'No Cookie'
+
+ def test_cookie_for_different_path(self):
+ c = Client(cookie_app)
+ c.open('/path1')
+ appiter, code, headers = c.open('/path2')
+ assert ''.join(appiter) == 'test=test'
+
+ def test_environ_builder_basics(self):
+ b = EnvironBuilder()
+ assert b.content_type is None
+ b.method = 'POST'
+ assert b.content_type == 'application/x-www-form-urlencoded'
+ b.files.add_file('test', StringIO('test contents'), 'test.txt')
+ assert b.files['test'].content_type == 'text/plain'
+ assert b.content_type == 'multipart/form-data'
+ b.form['test'] = 'normal value'
+
+ req = b.get_request()
+ b.close()
+
+ assert req.url == 'http://localhost/'
+ assert req.method == 'POST'
+ assert req.form['test'] == 'normal value'
+ assert req.files['test'].content_type == 'text/plain'
+ assert req.files['test'].filename == 'test.txt'
+ assert req.files['test'].read() == 'test contents'
+
+ def test_environ_builder_headers(self):
+ b = EnvironBuilder(environ_base={'HTTP_USER_AGENT': 'Foo/0.1'},
+ environ_overrides={'wsgi.version': (1, 1)})
+ b.headers['X-Suck-My-Dick'] = 'very well sir'
+ env = b.get_environ()
+ assert env['HTTP_USER_AGENT'] == 'Foo/0.1'
+ assert env['HTTP_X_SUCK_MY_DICK'] == 'very well sir'
+ assert env['wsgi.version'] == (1, 1)
+
+ b.headers['User-Agent'] = 'Bar/1.0'
+ env = b.get_environ()
+ assert env['HTTP_USER_AGENT'] == 'Bar/1.0'
+
+ def test_environ_builder_paths(self):
+ b = EnvironBuilder(path='/foo', base_url='http://example.com/')
+ assert b.base_url == 'http://example.com/'
+ assert b.path == '/foo'
+ assert b.script_root == ''
+ assert b.host == 'example.com'
+
+ b = EnvironBuilder(path='/foo', base_url='http://example.com/bar')
+ assert b.base_url == 'http://example.com/bar/'
+ assert b.path == '/foo'
+ assert b.script_root == '/bar'
+ assert b.host == 'example.com'
+
+ b.host = 'localhost'
+ assert b.base_url == 'http://localhost/bar/'
+ b.base_url = 'http://localhost:8080/'
+ assert b.host == 'localhost:8080'
+ assert b.server_name == 'localhost'
+ assert b.server_port == 8080
+
+ b.host = 'foo.invalid'
+ b.url_scheme = 'https'
+ b.script_root = '/test'
+ env = b.get_environ()
+ assert env['SERVER_NAME'] == 'foo.invalid'
+ assert env['SERVER_PORT'] == '443'
+ assert env['SCRIPT_NAME'] == '/test'
+ assert env['PATH_INFO'] == '/foo'
+ assert env['HTTP_HOST'] == 'foo.invalid'
+ assert env['wsgi.url_scheme'] == 'https'
+ assert b.base_url == 'https://foo.invalid/test/'
+
+ def test_environ_builder_content_type(self):
+ builder = EnvironBuilder()
+ assert builder.content_type is None
+ builder.method = 'POST'
+ assert builder.content_type == 'application/x-www-form-urlencoded'
+ builder.form['foo'] = 'bar'
+ assert builder.content_type == 'application/x-www-form-urlencoded'
+ builder.files.add_file('blafasel', StringIO('foo'), 'test.txt')
+ assert builder.content_type == 'multipart/form-data'
+ req = builder.get_request()
+ assert req.form['foo'] == 'bar'
+ assert req.files['blafasel'].read() == 'foo'
+
+ def test_environ_builder_stream_switch(self):
+ d = MultiDict(dict(foo=u'bar', blub=u'blah', hu=u'hum'))
+ for use_tempfile in False, True:
+ stream, length, boundary = stream_encode_multipart(
+ d, use_tempfile, threshold=150)
+ assert isinstance(stream, OutputType) != use_tempfile
+
+ form = parse_form_data({'wsgi.input': stream, 'CONTENT_LENGTH': str(length),
+ 'CONTENT_TYPE': 'multipart/form-data; boundary="%s"' %
+ boundary})[1]
+ assert form == d
+
+ def test_create_environ(self):
+ env = create_environ('/foo?bar=baz', 'http://example.org/')
+ expected = {
+ 'wsgi.multiprocess': False,
+ 'wsgi.version': (1, 0),
+ 'wsgi.run_once': False,
+ 'wsgi.errors': sys.stderr,
+ 'wsgi.multithread': False,
+ 'wsgi.url_scheme': 'http',
+ 'SCRIPT_NAME': '',
+ 'CONTENT_TYPE': '',
+ 'CONTENT_LENGTH': '0',
+ 'SERVER_NAME': 'example.org',
+ 'REQUEST_METHOD': 'GET',
+ 'HTTP_HOST': 'example.org',
+ 'PATH_INFO': '/foo',
+ 'SERVER_PORT': '80',
+ 'SERVER_PROTOCOL': 'HTTP/1.1',
+ 'QUERY_STRING': 'bar=baz'
+ }
+ for key, value in expected.iteritems():
+ assert env[key] == value
+ assert env['wsgi.input'].read(0) == ''
+
+ assert create_environ('/foo', 'http://example.com/')['SCRIPT_NAME'] == ''
+
+ def test_file_closing(self):
+ closed = []
+ class SpecialInput(object):
+ def read(self):
+ return ''
+ def close(self):
+ closed.append(self)
+
+ env = create_environ(data={'foo': SpecialInput()})
+ assert len(closed) == 1
+ builder = EnvironBuilder()
+ builder.files.add_file('blah', SpecialInput())
+ builder.close()
+ assert len(closed) == 2
+
+ def test_follow_redirect(self):
+ env = create_environ('/', base_url='http://localhost')
+ c = Client(redirect_with_get_app)
+ appiter, code, headers = c.open(environ_overrides=env, follow_redirects=True)
+ assert code == '200 OK'
+ assert ''.join(appiter) == 'current url: http://localhost/some/redirect/'
+
+ # Test that the :cls:`Client` is aware of user defined response wrappers
+ c = Client(redirect_with_get_app, response_wrapper=BaseResponse)
+ resp = c.get('/', follow_redirects=True)
+ assert resp.status_code == 200
+ assert resp.data == 'current url: http://localhost/some/redirect/'
+
+ # test with URL other than '/' to make sure redirected URL's are correct
+ c = Client(redirect_with_get_app, response_wrapper=BaseResponse)
+ resp = c.get('/first/request', follow_redirects=True)
+ assert resp.status_code == 200
+ assert resp.data == 'current url: http://localhost/some/redirect/'
+
+ def test_follow_external_redirect(self):
+ env = create_environ('/', base_url='http://localhost')
+ c = Client(external_redirect_demo_app)
+ self.assert_raises(RuntimeError, lambda:
+ c.get(environ_overrides=env, follow_redirects=True))
+
+ def test_follow_external_redirect_on_same_subdomain(self):
+ env = create_environ('/', base_url='http://example.com')
+ c = Client(external_subdomain_redirect_demo_app, allow_subdomain_redirects=True)
+ c.get(environ_overrides=env, follow_redirects=True)
+
+ # check that this does not work for real external domains
+ env = create_environ('/', base_url='http://localhost')
+ self.assert_raises(RuntimeError, lambda:
+ c.get(environ_overrides=env, follow_redirects=True))
+
+ # check that subdomain redirects fail if no `allow_subdomain_redirects` is applied
+ c = Client(external_subdomain_redirect_demo_app)
+ self.assert_raises(RuntimeError, lambda:
+ c.get(environ_overrides=env, follow_redirects=True))
+
+ def test_follow_redirect_loop(self):
+ c = Client(redirect_loop_app, response_wrapper=BaseResponse)
+ with self.assert_raises(ClientRedirectError):
+ resp = c.get('/', follow_redirects=True)
+
+ def test_follow_redirect_with_post(self):
+ c = Client(redirect_with_post_app, response_wrapper=BaseResponse)
+ resp = c.post('/', follow_redirects=True, data='foo=blub+hehe&blah=42')
+ assert resp.status_code == 200
+ assert resp.data == 'current url: http://localhost/some/redirect/'
+
+ def test_path_info_script_name_unquoting(self):
+ def test_app(environ, start_response):
+ start_response('200 OK', [('Content-Type', 'text/plain')])
+ return [environ['PATH_INFO'] + '\n' + environ['SCRIPT_NAME']]
+ c = Client(test_app, response_wrapper=BaseResponse)
+ resp = c.get('/foo%40bar')
+ assert resp.data == '/foo@bar\n'
+ c = Client(test_app, response_wrapper=BaseResponse)
+ resp = c.get('/foo%40bar', 'http://localhost/bar%40baz')
+ assert resp.data == '/foo@bar\n/bar@baz'
+
+ def test_multi_value_submit(self):
+ c = Client(multi_value_post_app, response_wrapper=BaseResponse)
+ data = {
+ 'field': ['val1','val2']
+ }
+ resp = c.post('/', data=data)
+ assert resp.status_code == 200
+ c = Client(multi_value_post_app, response_wrapper=BaseResponse)
+ data = MultiDict({
+ 'field': ['val1','val2']
+ })
+ resp = c.post('/', data=data)
+ assert resp.status_code == 200
+
+ def test_iri_support(self):
+ b = EnvironBuilder(u'/föö-bar', base_url=u'http://☃.net/')
+ assert b.path == '/f%C3%B6%C3%B6-bar'
+ assert b.base_url == 'http://xn--n3h.net/'
+
+ def test_run_wsgi_apps(self):
+ def simple_app(environ, start_response):
+ start_response('200 OK', [('Content-Type', 'text/html')])
+ return ['Hello World!']
+ app_iter, status, headers = run_wsgi_app(simple_app, {})
+ assert status == '200 OK'
+ assert headers == [('Content-Type', 'text/html')]
+ assert app_iter == ['Hello World!']
+
+ def yielding_app(environ, start_response):
+ start_response('200 OK', [('Content-Type', 'text/html')])
+ yield 'Hello '
+ yield 'World!'
+ app_iter, status, headers = run_wsgi_app(yielding_app, {})
+ assert status == '200 OK'
+ assert headers == [('Content-Type', 'text/html')]
+ assert list(app_iter) == ['Hello ', 'World!']
+
+ def test_multiple_cookies(self):
+ @Request.application
+ def test_app(request):
+ response = Response(repr(sorted(request.cookies.items())))
+ response.set_cookie('test1', 'foo')
+ response.set_cookie('test2', 'bar')
+ return response
+ client = Client(test_app, Response)
+ resp = client.get('/')
+ assert resp.data == '[]'
+ resp = client.get('/')
+ assert resp.data == "[('test1', u'foo'), ('test2', u'bar')]"
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/urls.py b/websdk/werkzeug/testsuite/urls.py
new file mode 100644
index 0000000..8c908b8
--- /dev/null
+++ b/websdk/werkzeug/testsuite/urls.py
@@ -0,0 +1,168 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.urls
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ URL helper tests.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+import unittest
+from StringIO import StringIO
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug.datastructures import OrderedMultiDict
+from werkzeug import urls
+
+
+class URLsTestCase(WerkzeugTestCase):
+
+ def test_quoting(self):
+ assert urls.url_quote(u'\xf6\xe4\xfc') == '%C3%B6%C3%A4%C3%BC'
+ assert urls.url_unquote(urls.url_quote(u'#%="\xf6')) == u'#%="\xf6'
+ assert urls.url_quote_plus('foo bar') == 'foo+bar'
+ assert urls.url_unquote_plus('foo+bar') == 'foo bar'
+ assert urls.url_encode({'a': None, 'b': 'foo bar'}) == 'b=foo+bar'
+ assert urls.url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffsklärung)') == \
+ 'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'
+
+ def test_url_decoding(self):
+ x = urls.url_decode('foo=42&bar=23&uni=H%C3%A4nsel')
+ assert x['foo'] == '42'
+ assert x['bar'] == '23'
+ assert x['uni'] == u'Hänsel'
+
+ x = urls.url_decode('foo=42;bar=23;uni=H%C3%A4nsel', separator=';')
+ assert x['foo'] == '42'
+ assert x['bar'] == '23'
+ assert x['uni'] == u'Hänsel'
+
+ x = urls.url_decode('%C3%9Ch=H%C3%A4nsel', decode_keys=True)
+ assert x[u'Üh'] == u'Hänsel'
+
+ def test_streamed_url_decoding(self):
+ item1 = 'a' * 100000
+ item2 = 'b' * 400
+ string = 'a=%s&b=%s&c=%s' % (item1, item2, item2)
+ gen = urls.url_decode_stream(StringIO(string), limit=len(string),
+ return_iterator=True)
+ self.assert_equal(gen.next(), ('a', item1))
+ self.assert_equal(gen.next(), ('b', item2))
+ self.assert_equal(gen.next(), ('c', item2))
+ self.assert_raises(StopIteration, gen.next)
+
+ def test_url_encoding(self):
+ assert urls.url_encode({'foo': 'bar 45'}) == 'foo=bar+45'
+ d = {'foo': 1, 'bar': 23, 'blah': u'Hänsel'}
+ assert urls.url_encode(d, sort=True) == 'bar=23&blah=H%C3%A4nsel&foo=1'
+ assert urls.url_encode(d, sort=True, separator=';') == 'bar=23;blah=H%C3%A4nsel;foo=1'
+
+ def test_sorted_url_encode(self):
+ assert urls.url_encode({"a": 42, "b": 23, 1: 1, 2: 2}, sort=True) == '1=1&2=2&a=42&b=23'
+ assert urls.url_encode({'A': 1, 'a': 2, 'B': 3, 'b': 4}, sort=True,
+ key=lambda x: x[0].lower()) == 'A=1&a=2&B=3&b=4'
+
+ def test_streamed_url_encoding(self):
+ out = StringIO()
+ urls.url_encode_stream({'foo': 'bar 45'}, out)
+ self.assert_equal(out.getvalue(), 'foo=bar+45')
+
+ d = {'foo': 1, 'bar': 23, 'blah': u'Hänsel'}
+ out = StringIO()
+ urls.url_encode_stream(d, out, sort=True)
+ self.assert_equal(out.getvalue(), 'bar=23&blah=H%C3%A4nsel&foo=1')
+ out = StringIO()
+ urls.url_encode_stream(d, out, sort=True, separator=';')
+ self.assert_equal(out.getvalue(), 'bar=23;blah=H%C3%A4nsel;foo=1')
+
+ gen = urls.url_encode_stream(d, sort=True)
+ self.assert_equal(gen.next(), 'bar=23')
+ self.assert_equal(gen.next(), 'blah=H%C3%A4nsel')
+ self.assert_equal(gen.next(), 'foo=1')
+ self.assert_raises(StopIteration, gen.next)
+
+ def test_url_fixing(self):
+ x = urls.url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffskl\xe4rung)')
+ assert x == 'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'
+
+ x = urls.url_fix('http://example.com/?foo=%2f%2f')
+ assert x == 'http://example.com/?foo=%2f%2f'
+
+ def test_iri_support(self):
+ self.assert_raises(UnicodeError, urls.uri_to_iri, u'http://föö.com/')
+ self.assert_raises(UnicodeError, urls.iri_to_uri, 'http://föö.com/')
+ assert urls.uri_to_iri('http://xn--n3h.net/') == u'http://\u2603.net/'
+ assert urls.uri_to_iri('http://%C3%BCser:p%C3%A4ssword@xn--n3h.net/p%C3%A5th') == \
+ u'http://\xfcser:p\xe4ssword@\u2603.net/p\xe5th'
+ assert urls.iri_to_uri(u'http://☃.net/') == 'http://xn--n3h.net/'
+ assert urls.iri_to_uri(u'http://üser:pässword@☃.net/påth') == \
+ 'http://%C3%BCser:p%C3%A4ssword@xn--n3h.net/p%C3%A5th'
+
+ assert urls.uri_to_iri('http://test.com/%3Fmeh?foo=%26%2F') == \
+ u'http://test.com/%3Fmeh?foo=%26%2F'
+
+ # this should work as well, might break on 2.4 because of a broken
+ # idna codec
+ assert urls.uri_to_iri('/foo') == u'/foo'
+ assert urls.iri_to_uri(u'/foo') == '/foo'
+
+ def test_ordered_multidict_encoding(self):
+ d = OrderedMultiDict()
+ d.add('foo', 1)
+ d.add('foo', 2)
+ d.add('foo', 3)
+ d.add('bar', 0)
+ d.add('foo', 4)
+ assert urls.url_encode(d) == 'foo=1&foo=2&foo=3&bar=0&foo=4'
+
+ def test_href(self):
+ x = urls.Href('http://www.example.com/')
+ assert x('foo') == 'http://www.example.com/foo'
+ assert x.foo('bar') == 'http://www.example.com/foo/bar'
+ assert x.foo('bar', x=42) == 'http://www.example.com/foo/bar?x=42'
+ assert x.foo('bar', class_=42) == 'http://www.example.com/foo/bar?class=42'
+ assert x.foo('bar', {'class': 42}) == 'http://www.example.com/foo/bar?class=42'
+ self.assert_raises(AttributeError, lambda: x.__blah__)
+
+ x = urls.Href('blah')
+ assert x.foo('bar') == 'blah/foo/bar'
+
+ self.assert_raises(TypeError, x.foo, {"foo": 23}, x=42)
+
+ x = urls.Href('')
+ assert x('foo') == 'foo'
+
+ def test_href_url_join(self):
+ x = urls.Href('test')
+ assert x('foo:bar') == 'test/foo:bar'
+ assert x('http://example.com/') == 'test/http://example.com/'
+
+ if 0:
+ # stdlib bug? :(
+ def test_href_past_root(self):
+ base_href = urls.Href('http://www.blagga.com/1/2/3')
+ assert base_href('../foo') == 'http://www.blagga.com/1/2/foo'
+ assert base_href('../../foo') == 'http://www.blagga.com/1/foo'
+ assert base_href('../../../foo') == 'http://www.blagga.com/foo'
+ assert base_href('../../../../foo') == 'http://www.blagga.com/foo'
+ assert base_href('../../../../../foo') == 'http://www.blagga.com/foo'
+ assert base_href('../../../../../../foo') == 'http://www.blagga.com/foo'
+
+ def test_url_unquote_plus_unicode(self):
+ # was broken in 0.6
+ assert urls.url_unquote_plus(u'\x6d') == u'\x6d'
+ assert type(urls.url_unquote_plus(u'\x6d')) is unicode
+
+ def test_quoting_of_local_urls(self):
+ rv = urls.iri_to_uri(u'/foo\x8f')
+ assert rv == '/foo%C2%8F'
+ assert type(rv) is str
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(URLsTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/utils.py b/websdk/werkzeug/testsuite/utils.py
new file mode 100644
index 0000000..5ef953c
--- /dev/null
+++ b/websdk/werkzeug/testsuite/utils.py
@@ -0,0 +1,255 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.utils
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ General utilities.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import unittest
+from datetime import datetime
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug import utils
+from werkzeug.datastructures import Headers
+from werkzeug.http import parse_date, http_date
+from werkzeug.wrappers import BaseResponse
+from werkzeug.test import Client, run_wsgi_app
+
+
+class GeneralUtilityTestCase(WerkzeugTestCase):
+
+ def test_redirect(self):
+ resp = utils.redirect(u'/füübär')
+ assert '/f%C3%BC%C3%BCb%C3%A4r' in resp.data
+ assert resp.headers['Location'] == '/f%C3%BC%C3%BCb%C3%A4r'
+ assert resp.status_code == 302
+
+ resp = utils.redirect(u'http://☃.net/', 307)
+ assert 'http://xn--n3h.net/' in resp.data
+ assert resp.headers['Location'] == 'http://xn--n3h.net/'
+ assert resp.status_code == 307
+
+ resp = utils.redirect('http://example.com/', 305)
+ assert resp.headers['Location'] == 'http://example.com/'
+ assert resp.status_code == 305
+
+ def test_cached_property(self):
+ foo = []
+ class A(object):
+ def prop(self):
+ foo.append(42)
+ return 42
+ prop = utils.cached_property(prop)
+
+ a = A()
+ p = a.prop
+ q = a.prop
+ assert p == q == 42
+ assert foo == [42]
+
+ foo = []
+ class A(object):
+ def _prop(self):
+ foo.append(42)
+ return 42
+ prop = utils.cached_property(_prop, name='prop')
+ del _prop
+
+ a = A()
+ p = a.prop
+ q = a.prop
+ assert p == q == 42
+ assert foo == [42]
+
+ def test_environ_property(self):
+ class A(object):
+ environ = {'string': 'abc', 'number': '42'}
+
+ string = utils.environ_property('string')
+ missing = utils.environ_property('missing', 'spam')
+ read_only = utils.environ_property('number')
+ number = utils.environ_property('number', load_func=int)
+ broken_number = utils.environ_property('broken_number', load_func=int)
+ date = utils.environ_property('date', None, parse_date, http_date,
+ read_only=False)
+ foo = utils.environ_property('foo')
+
+ a = A()
+ assert a.string == 'abc'
+ assert a.missing == 'spam'
+ def test_assign():
+ a.read_only = 'something'
+ self.assert_raises(AttributeError, test_assign)
+ assert a.number == 42
+ assert a.broken_number == None
+ assert a.date is None
+ a.date = datetime(2008, 1, 22, 10, 0, 0, 0)
+ assert a.environ['date'] == 'Tue, 22 Jan 2008 10:00:00 GMT'
+
+ def test_escape(self):
+ class Foo(str):
+ def __html__(self):
+ return unicode(self)
+ assert utils.escape(None) == ''
+ assert utils.escape(42) == '42'
+ assert utils.escape('<>') == '&lt;&gt;'
+ assert utils.escape('"foo"') == '"foo"'
+ assert utils.escape('"foo"', True) == '&quot;foo&quot;'
+ assert utils.escape(Foo('<foo>')) == '<foo>'
+
+ def test_unescape(self):
+ assert utils.unescape('&lt;&auml;&gt;') == u'<ä>'
+
+ def test_run_wsgi_app(self):
+ def foo(environ, start_response):
+ start_response('200 OK', [('Content-Type', 'text/plain')])
+ yield '1'
+ yield '2'
+ yield '3'
+
+ app_iter, status, headers = run_wsgi_app(foo, {})
+ assert status == '200 OK'
+ assert headers == [('Content-Type', 'text/plain')]
+ assert app_iter.next() == '1'
+ assert app_iter.next() == '2'
+ assert app_iter.next() == '3'
+ self.assert_raises(StopIteration, app_iter.next)
+
+ got_close = []
+ class CloseIter(object):
+ def __init__(self):
+ self.iterated = False
+ def __iter__(self):
+ return self
+ def close(self):
+ got_close.append(None)
+ def next(self):
+ if self.iterated:
+ raise StopIteration()
+ self.iterated = True
+ return 'bar'
+
+ def bar(environ, start_response):
+ start_response('200 OK', [('Content-Type', 'text/plain')])
+ return CloseIter()
+
+ app_iter, status, headers = run_wsgi_app(bar, {})
+ assert status == '200 OK'
+ assert headers == [('Content-Type', 'text/plain')]
+ assert app_iter.next() == 'bar'
+ self.assert_raises(StopIteration, app_iter.next)
+ app_iter.close()
+
+ assert run_wsgi_app(bar, {}, True)[0] == ['bar']
+
+ assert len(got_close) == 2
+
+ def test_import_string(self):
+ import cgi
+ from werkzeug.debug import DebuggedApplication
+ assert utils.import_string('cgi.escape') is cgi.escape
+ assert utils.import_string(u'cgi.escape') is cgi.escape
+ assert utils.import_string('cgi:escape') is cgi.escape
+ assert utils.import_string('XXXXXXXXXXXX', True) is None
+ assert utils.import_string('cgi.XXXXXXXXXXXX', True) is None
+ assert utils.import_string(u'cgi.escape') is cgi.escape
+ assert utils.import_string(u'werkzeug.debug.DebuggedApplication') is DebuggedApplication
+ self.assert_raises(ImportError, utils.import_string, 'XXXXXXXXXXXXXXXX')
+ self.assert_raises(ImportError, utils.import_string, 'cgi.XXXXXXXXXX')
+
+ def test_find_modules(self):
+ assert list(utils.find_modules('werkzeug.debug')) == \
+ ['werkzeug.debug.console', 'werkzeug.debug.repr',
+ 'werkzeug.debug.tbtools']
+
+ def test_html_builder(self):
+ html = utils.html
+ xhtml = utils.xhtml
+ assert html.p('Hello World') == '<p>Hello World</p>'
+ assert html.a('Test', href='#') == '<a href="#">Test</a>'
+ assert html.br() == '<br>'
+ assert xhtml.br() == '<br />'
+ assert html.img(src='foo') == '<img src="foo">'
+ assert xhtml.img(src='foo') == '<img src="foo" />'
+ assert html.html(
+ html.head(
+ html.title('foo'),
+ html.script(type='text/javascript')
+ )
+ ) == '<html><head><title>foo</title><script type="text/javascript">' \
+ '</script></head></html>'
+ assert html('<foo>') == '&lt;foo&gt;'
+ assert html.input(disabled=True) == '<input disabled>'
+ assert xhtml.input(disabled=True) == '<input disabled="disabled" />'
+ assert html.input(disabled='') == '<input>'
+ assert xhtml.input(disabled='') == '<input />'
+ assert html.input(disabled=None) == '<input>'
+ assert xhtml.input(disabled=None) == '<input />'
+ assert html.script('alert("Hello World");') == '<script>' \
+ 'alert("Hello World");</script>'
+ assert xhtml.script('alert("Hello World");') == '<script>' \
+ '/*<![CDATA[*/alert("Hello World");/*]]>*/</script>'
+
+ def test_validate_arguments(self):
+ take_none = lambda: None
+ take_two = lambda a, b: None
+ take_two_one_default = lambda a, b=0: None
+
+ assert utils.validate_arguments(take_two, (1, 2,), {}) == ((1, 2), {})
+ assert utils.validate_arguments(take_two, (1,), {'b': 2}) == ((1, 2), {})
+ assert utils.validate_arguments(take_two_one_default, (1,), {}) == ((1, 0), {})
+ assert utils.validate_arguments(take_two_one_default, (1, 2), {}) == ((1, 2), {})
+
+ self.assert_raises(utils.ArgumentValidationError,
+ utils.validate_arguments, take_two, (), {})
+
+ assert utils.validate_arguments(take_none, (1, 2,), {'c': 3}) == ((), {})
+ self.assert_raises(utils.ArgumentValidationError,
+ utils.validate_arguments, take_none, (1,), {}, drop_extra=False)
+ self.assert_raises(utils.ArgumentValidationError,
+ utils.validate_arguments, take_none, (), {'a': 1}, drop_extra=False)
+
+ def test_header_set_duplication_bug(self):
+ headers = Headers([
+ ('Content-Type', 'text/html'),
+ ('Foo', 'bar'),
+ ('Blub', 'blah')
+ ])
+ headers['blub'] = 'hehe'
+ headers['blafasel'] = 'humm'
+ assert headers == Headers([
+ ('Content-Type', 'text/html'),
+ ('Foo', 'bar'),
+ ('blub', 'hehe'),
+ ('blafasel', 'humm')
+ ])
+
+ def test_append_slash_redirect(self):
+ def app(env, sr):
+ return utils.append_slash_redirect(env)(env, sr)
+ client = Client(app, BaseResponse)
+ response = client.get('foo', base_url='http://example.org/app')
+ assert response.status_code == 301
+ assert response.headers['Location'] == 'http://example.org/app/foo/'
+
+ def test_cached_property_doc(self):
+ @utils.cached_property
+ def foo():
+ """testing"""
+ return 42
+ assert foo.__doc__ == 'testing'
+ assert foo.__name__ == 'foo'
+ assert foo.__module__ == __name__
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(GeneralUtilityTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/wrappers.py b/websdk/werkzeug/testsuite/wrappers.py
new file mode 100644
index 0000000..b18f891
--- /dev/null
+++ b/websdk/werkzeug/testsuite/wrappers.py
@@ -0,0 +1,662 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.wrappers
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests for the response and request objects.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+import unittest
+import pickle
+from StringIO import StringIO
+from datetime import datetime
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug import wrappers
+from werkzeug.datastructures import MultiDict, ImmutableOrderedMultiDict, \
+ ImmutableList, ImmutableTypeConversionDict, CharsetAccept, \
+ CombinedMultiDict
+from werkzeug.test import Client, create_environ, run_wsgi_app
+
+
+class RequestTestResponse(wrappers.BaseResponse):
+ """Subclass of the normal response class we use to test response
+ and base classes. Has some methods to test if things in the
+ response match.
+ """
+
+ def __init__(self, response, status, headers):
+ wrappers.BaseResponse.__init__(self, response, status, headers)
+ self.body_data = pickle.loads(self.data)
+
+ def __getitem__(self, key):
+ return self.body_data[key]
+
+
+def request_demo_app(environ, start_response):
+ request = wrappers.BaseRequest(environ)
+ assert 'werkzeug.request' in environ
+ start_response('200 OK', [('Content-Type', 'text/plain')])
+ return [pickle.dumps({
+ 'args': request.args,
+ 'args_as_list': request.args.lists(),
+ 'form': request.form,
+ 'form_as_list': request.form.lists(),
+ 'environ': prepare_environ_pickle(request.environ),
+ 'data': request.data
+ })]
+
+
+def prepare_environ_pickle(environ):
+ result = {}
+ for key, value in environ.iteritems():
+ try:
+ pickle.dumps((key, value))
+ except Exception:
+ continue
+ result[key] = value
+ return result
+
+
+class WrappersTestCase(WerkzeugTestCase):
+
+ def assert_environ(self, environ, method):
+ assert environ['REQUEST_METHOD'] == method
+ assert environ['PATH_INFO'] == '/'
+ assert environ['SCRIPT_NAME'] == ''
+ assert environ['SERVER_NAME'] == 'localhost'
+ assert environ['wsgi.version'] == (1, 0)
+ assert environ['wsgi.url_scheme'] == 'http'
+
+ def test_base_request(self):
+ client = Client(request_demo_app, RequestTestResponse)
+
+ # get requests
+ response = client.get('/?foo=bar&foo=hehe')
+ assert response['args'] == MultiDict([('foo', 'bar'), ('foo', 'hehe')])
+ assert response['args_as_list'] == [('foo', ['bar', 'hehe'])]
+ assert response['form'] == MultiDict()
+ assert response['form_as_list'] == []
+ assert response['data'] == ''
+ self.assert_environ(response['environ'], 'GET')
+
+ # post requests with form data
+ response = client.post('/?blub=blah', data='foo=blub+hehe&blah=42',
+ content_type='application/x-www-form-urlencoded')
+ assert response['args'] == MultiDict([('blub', 'blah')])
+ assert response['args_as_list'] == [('blub', ['blah'])]
+ assert response['form'] == MultiDict([('foo', 'blub hehe'), ('blah', '42')])
+ assert response['data'] == ''
+ # currently we do not guarantee that the values are ordered correctly
+ # for post data.
+ ## assert response['form_as_list'] == [('foo', ['blub hehe']), ('blah', ['42'])]
+ self.assert_environ(response['environ'], 'POST')
+
+ # patch requests with form data
+ response = client.patch('/?blub=blah', data='foo=blub+hehe&blah=42',
+ content_type='application/x-www-form-urlencoded')
+ assert response['args'] == MultiDict([('blub', 'blah')])
+ assert response['args_as_list'] == [('blub', ['blah'])]
+ assert response['form'] == MultiDict([('foo', 'blub hehe'), ('blah', '42')])
+ assert response['data'] == ''
+ self.assert_environ(response['environ'], 'PATCH')
+
+ # post requests with json data
+ json = '{"foo": "bar", "blub": "blah"}'
+ response = client.post('/?a=b', data=json, content_type='application/json')
+ assert response['data'] == json
+ assert response['args'] == MultiDict([('a', 'b')])
+ assert response['form'] == MultiDict()
+
+ def test_access_route(self):
+ req = wrappers.Request.from_values(headers={
+ 'X-Forwarded-For': '192.168.1.2, 192.168.1.1'
+ })
+ req.environ['REMOTE_ADDR'] = '192.168.1.3'
+ assert req.access_route == ['192.168.1.2', '192.168.1.1']
+ assert req.remote_addr == '192.168.1.3'
+
+ req = wrappers.Request.from_values()
+ req.environ['REMOTE_ADDR'] = '192.168.1.3'
+ assert req.access_route == ['192.168.1.3']
+
+ def test_url_request_descriptors(self):
+ req = wrappers.Request.from_values('/bar?foo=baz', 'http://example.com/test')
+ assert req.path == u'/bar'
+ assert req.script_root == u'/test'
+ assert req.url == 'http://example.com/test/bar?foo=baz'
+ assert req.base_url == 'http://example.com/test/bar'
+ assert req.url_root == 'http://example.com/test/'
+ assert req.host_url == 'http://example.com/'
+ assert req.host == 'example.com'
+ assert req.scheme == 'http'
+
+ req = wrappers.Request.from_values('/bar?foo=baz', 'https://example.com/test')
+ assert req.scheme == 'https'
+
+ def test_authorization_mixin(self):
+ request = wrappers.Request.from_values(headers={
+ 'Authorization': 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
+ })
+ a = request.authorization
+ assert a.type == 'basic'
+ assert a.username == 'Aladdin'
+ assert a.password == 'open sesame'
+
+ def test_base_response(self):
+ # unicode
+ response = wrappers.BaseResponse(u'öäü')
+ assert response.data == 'öäü'
+
+ # writing
+ response = wrappers.Response('foo')
+ response.stream.write('bar')
+ assert response.data == 'foobar'
+
+ # set cookie
+ response = wrappers.BaseResponse()
+ response.set_cookie('foo', 'bar', 60, 0, '/blub', 'example.org', False)
+ assert response.headers.to_list() == [
+ ('Content-Type', 'text/plain; charset=utf-8'),
+ ('Set-Cookie', 'foo=bar; Domain=example.org; expires=Thu, '
+ '01-Jan-1970 00:00:00 GMT; Max-Age=60; Path=/blub')
+ ]
+
+ # delete cookie
+ response = wrappers.BaseResponse()
+ response.delete_cookie('foo')
+ assert response.headers.to_list() == [
+ ('Content-Type', 'text/plain; charset=utf-8'),
+ ('Set-Cookie', 'foo=; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/')
+ ]
+
+ # close call forwarding
+ closed = []
+ class Iterable(object):
+ def next(self):
+ raise StopIteration()
+ def __iter__(self):
+ return self
+ def close(self):
+ closed.append(True)
+ response = wrappers.BaseResponse(Iterable())
+ response.call_on_close(lambda: closed.append(True))
+ app_iter, status, headers = run_wsgi_app(response,
+ create_environ(),
+ buffered=True)
+ assert status == '200 OK'
+ assert ''.join(app_iter) == ''
+ assert len(closed) == 2
+
+ def test_response_status_codes(self):
+ response = wrappers.BaseResponse()
+ response.status_code = 404
+ assert response.status == '404 NOT FOUND'
+ response.status = '200 OK'
+ assert response.status_code == 200
+ response.status = '999 WTF'
+ assert response.status_code == 999
+ response.status_code = 588
+ assert response.status_code == 588
+ assert response.status == '588 UNKNOWN'
+ response.status = 'wtf'
+ assert response.status_code == 0
+
+ def test_type_forcing(self):
+ def wsgi_application(environ, start_response):
+ start_response('200 OK', [('Content-Type', 'text/html')])
+ return ['Hello World!']
+ base_response = wrappers.BaseResponse('Hello World!', content_type='text/html')
+
+ class SpecialResponse(wrappers.Response):
+ def foo(self):
+ return 42
+
+ # good enough for this simple application, but don't ever use that in
+ # real world examples!
+ fake_env = {}
+
+ for orig_resp in wsgi_application, base_response:
+ response = SpecialResponse.force_type(orig_resp, fake_env)
+ assert response.__class__ is SpecialResponse
+ assert response.foo() == 42
+ assert response.data == 'Hello World!'
+ assert response.content_type == 'text/html'
+
+ # without env, no arbitrary conversion
+ self.assert_raises(TypeError, SpecialResponse.force_type, wsgi_application)
+
+ def test_accept_mixin(self):
+ request = wrappers.Request({
+ 'HTTP_ACCEPT': 'text/xml,application/xml,application/xhtml+xml,'
+ 'text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
+ 'HTTP_ACCEPT_CHARSET': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
+ 'HTTP_ACCEPT_ENCODING': 'gzip,deflate',
+ 'HTTP_ACCEPT_LANGUAGE': 'en-us,en;q=0.5'
+ })
+ assert request.accept_mimetypes == CharsetAccept([
+ ('text/xml', 1), ('image/png', 1), ('application/xml', 1),
+ ('application/xhtml+xml', 1), ('text/html', 0.9),
+ ('text/plain', 0.8), ('*/*', 0.5)
+ ])
+ assert request.accept_charsets == CharsetAccept([
+ ('ISO-8859-1', 1), ('utf-8', 0.7), ('*', 0.7)
+ ])
+ assert request.accept_encodings == CharsetAccept([('gzip', 1), ('deflate', 1)])
+ assert request.accept_languages == CharsetAccept([('en-us', 1), ('en', 0.5)])
+
+ request = wrappers.Request({'HTTP_ACCEPT': ''})
+ assert request.accept_mimetypes == CharsetAccept()
+
+ def test_etag_request_mixin(self):
+ request = wrappers.Request({
+ 'HTTP_CACHE_CONTROL': 'no-store, no-cache',
+ 'HTTP_IF_MATCH': 'w/"foo", bar, "baz"',
+ 'HTTP_IF_NONE_MATCH': 'w/"foo", bar, "baz"',
+ 'HTTP_IF_MODIFIED_SINCE': 'Tue, 22 Jan 2008 11:18:44 GMT',
+ 'HTTP_IF_UNMODIFIED_SINCE': 'Tue, 22 Jan 2008 11:18:44 GMT'
+ })
+ assert request.cache_control.no_store
+ assert request.cache_control.no_cache
+
+ for etags in request.if_match, request.if_none_match:
+ assert etags('bar')
+ assert etags.contains_raw('w/"foo"')
+ assert etags.contains_weak('foo')
+ assert not etags.contains('foo')
+
+ assert request.if_modified_since == datetime(2008, 1, 22, 11, 18, 44)
+ assert request.if_unmodified_since == datetime(2008, 1, 22, 11, 18, 44)
+
+ def test_user_agent_mixin(self):
+ user_agents = [
+ ('Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.11) '
+ 'Gecko/20071127 Firefox/2.0.0.11', 'firefox', 'macos', '2.0.0.11',
+ 'en-US'),
+ ('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; de-DE) Opera 8.54',
+ 'opera', 'windows', '8.54', 'de-DE'),
+ ('Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420 '
+ '(KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3',
+ 'safari', 'iphone', '419.3', 'en'),
+ ('Bot Googlebot/2.1 ( http://www.googlebot.com/bot.html)',
+ 'google', None, '2.1', None)
+ ]
+ for ua, browser, platform, version, lang in user_agents:
+ request = wrappers.Request({'HTTP_USER_AGENT': ua})
+ assert request.user_agent.browser == browser
+ assert request.user_agent.platform == platform
+ assert request.user_agent.version == version
+ assert request.user_agent.language == lang
+ assert bool(request.user_agent)
+ assert request.user_agent.to_header() == ua
+ assert str(request.user_agent) == ua
+
+ request = wrappers.Request({'HTTP_USER_AGENT': 'foo'})
+ assert not request.user_agent
+
+ def test_etag_response_mixin(self):
+ response = wrappers.Response('Hello World')
+ assert response.get_etag() == (None, None)
+ response.add_etag()
+ assert response.get_etag() == ('b10a8db164e0754105b7a99be72e3fe5', False)
+ assert not response.cache_control
+ response.cache_control.must_revalidate = True
+ response.cache_control.max_age = 60
+ response.headers['Content-Length'] = len(response.data)
+ assert response.headers['Cache-Control'] == 'must-revalidate, max-age=60'
+
+ assert 'date' not in response.headers
+ env = create_environ()
+ env.update({
+ 'REQUEST_METHOD': 'GET',
+ 'HTTP_IF_NONE_MATCH': response.get_etag()[0]
+ })
+ response.make_conditional(env)
+ assert 'date' in response.headers
+
+ # after the thing is invoked by the server as wsgi application
+ # (we're emulating this here), there must not be any entity
+ # headers left and the status code would have to be 304
+ resp = wrappers.Response.from_app(response, env)
+ assert resp.status_code == 304
+ assert not 'content-length' in resp.headers
+
+ # make sure date is not overriden
+ response = wrappers.Response('Hello World')
+ response.date = 1337
+ d = response.date
+ response.make_conditional(env)
+ assert response.date == d
+
+ # make sure content length is only set if missing
+ response = wrappers.Response('Hello World')
+ response.content_length = 999
+ response.make_conditional(env)
+ self.assert_equal(response.content_length, 999)
+
+ def test_etag_response_mixin_freezing(self):
+ class WithFreeze(wrappers.ETagResponseMixin, wrappers.BaseResponse):
+ pass
+ class WithoutFreeze(wrappers.BaseResponse, wrappers.ETagResponseMixin):
+ pass
+
+ response = WithFreeze('Hello World')
+ response.freeze()
+ assert response.get_etag() == (wrappers.generate_etag('Hello World'), False)
+ response = WithoutFreeze('Hello World')
+ response.freeze()
+ assert response.get_etag() == (None, None)
+ response = wrappers.Response('Hello World')
+ response.freeze()
+ assert response.get_etag() == (None, None)
+
+ def test_authenticate_mixin(self):
+ resp = wrappers.Response()
+ resp.www_authenticate.type = 'basic'
+ resp.www_authenticate.realm = 'Testing'
+ assert resp.headers['WWW-Authenticate'] == 'Basic realm="Testing"'
+ resp.www_authenticate.realm = None
+ resp.www_authenticate.type = None
+ assert 'WWW-Authenticate' not in resp.headers
+
+ def test_response_stream_mixin(self):
+ response = wrappers.Response()
+ response.stream.write('Hello ')
+ response.stream.write('World!')
+ assert response.response == ['Hello ', 'World!']
+ assert response.data == 'Hello World!'
+
+ def test_common_response_descriptors_mixin(self):
+ response = wrappers.Response()
+ response.mimetype = 'text/html'
+ assert response.mimetype == 'text/html'
+ assert response.content_type == 'text/html; charset=utf-8'
+ assert response.mimetype_params == {'charset': 'utf-8'}
+ response.mimetype_params['x-foo'] = 'yep'
+ del response.mimetype_params['charset']
+ assert response.content_type == 'text/html; x-foo=yep'
+
+ now = datetime.utcnow().replace(microsecond=0)
+
+ assert response.content_length is None
+ response.content_length = '42'
+ assert response.content_length == 42
+
+ for attr in 'date', 'age', 'expires':
+ assert getattr(response, attr) is None
+ setattr(response, attr, now)
+ assert getattr(response, attr) == now
+
+ assert response.retry_after is None
+ response.retry_after = now
+ assert response.retry_after == now
+
+ assert not response.vary
+ response.vary.add('Cookie')
+ response.vary.add('Content-Language')
+ assert 'cookie' in response.vary
+ assert response.vary.to_header() == 'Cookie, Content-Language'
+ response.headers['Vary'] = 'Content-Encoding'
+ assert response.vary.as_set() == set(['content-encoding'])
+
+ response.allow.update(['GET', 'POST'])
+ assert response.headers['Allow'] == 'GET, POST'
+
+ response.content_language.add('en-US')
+ response.content_language.add('fr')
+ assert response.headers['Content-Language'] == 'en-US, fr'
+
+
+ def test_common_request_descriptors_mixin(self):
+ request = wrappers.Request.from_values(content_type='text/html; charset=utf-8',
+ content_length='23',
+ headers={
+ 'Referer': 'http://www.example.com/',
+ 'Date': 'Sat, 28 Feb 2009 19:04:35 GMT',
+ 'Max-Forwards': '10',
+ 'Pragma': 'no-cache'
+ })
+
+ assert request.content_type == 'text/html; charset=utf-8'
+ assert request.mimetype == 'text/html'
+ assert request.mimetype_params == {'charset': 'utf-8'}
+ assert request.content_length == 23
+ assert request.referrer == 'http://www.example.com/'
+ assert request.date == datetime(2009, 2, 28, 19, 4, 35)
+ assert request.max_forwards == 10
+ assert 'no-cache' in request.pragma
+
+ def test_shallow_mode(self):
+ """Request object shallow mode"""
+ request = wrappers.Request({'QUERY_STRING': 'foo=bar'}, shallow=True)
+ assert request.args['foo'] == 'bar'
+ self.assert_raises(RuntimeError, lambda: request.form['foo'])
+
+ def test_form_parsing_failed(self):
+ data = (
+ '--blah\r\n'
+ )
+ data = wrappers.Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='multipart/form-data; boundary=foo',
+ method='POST')
+ assert not data.files
+ assert not data.form
+
+ def test_url_charset_reflection(self):
+ req = wrappers.Request.from_values()
+ req.charset = 'utf-7'
+ assert req.url_charset == 'utf-7'
+
+ def test_response_streamed(self):
+ r = wrappers.Response()
+ assert not r.is_streamed
+ r = wrappers.Response("Hello World")
+ assert not r.is_streamed
+ r = wrappers.Response(["foo", "bar"])
+ assert not r.is_streamed
+ def gen():
+ if 0:
+ yield None
+ r = wrappers.Response(gen())
+ assert r.is_streamed
+
+ def test_response_freeze(self):
+ def generate():
+ yield "foo"
+ yield "bar"
+ resp = wrappers.Response(generate())
+ resp.freeze()
+ assert resp.response == ['foo', 'bar']
+ assert resp.headers['content-length'] == '6'
+
+ def test_other_method_payload(self):
+ data = 'Hello World'
+ req = wrappers.Request.from_values(input_stream=StringIO(data),
+ content_length=len(data),
+ content_type='text/plain',
+ method='WHAT_THE_FUCK')
+ assert req.data == data
+ assert isinstance(req.stream, wrappers.LimitedStream)
+
+ def test_urlfication(self):
+ resp = wrappers.Response()
+ resp.headers['Location'] = u'http://üser:pässword@☃.net/påth'
+ resp.headers['Content-Location'] = u'http://☃.net/'
+ headers = resp.get_wsgi_headers(create_environ())
+ assert headers['location'] == \
+ 'http://%C3%BCser:p%C3%A4ssword@xn--n3h.net/p%C3%A5th'
+ assert headers['content-location'] == 'http://xn--n3h.net/'
+
+ def test_new_response_iterator_behavior(self):
+ req = wrappers.Request.from_values()
+ resp = wrappers.Response(u'Hello Wörld!')
+
+ def get_content_length(resp):
+ headers = wrappers.Headers.linked(resp.get_wsgi_headers(req.environ))
+ return headers.get('content-length', type=int)
+
+ def generate_items():
+ yield "Hello "
+ yield u"Wörld!"
+
+ # werkzeug encodes when set to `data` now, which happens
+ # if a string is passed to the response object.
+ assert resp.response == [u'Hello Wörld!'.encode('utf-8')]
+ assert resp.data == u'Hello Wörld!'.encode('utf-8')
+ assert get_content_length(resp) == 13
+ assert not resp.is_streamed
+ assert resp.is_sequence
+
+ # try the same for manual assignment
+ resp.data = u'Wörd'
+ assert resp.response == [u'Wörd'.encode('utf-8')]
+ assert resp.data == u'Wörd'.encode('utf-8')
+ assert get_content_length(resp) == 5
+ assert not resp.is_streamed
+ assert resp.is_sequence
+
+ # automatic generator sequence conversion
+ resp.response = generate_items()
+ assert resp.is_streamed
+ assert not resp.is_sequence
+ assert resp.data == u'Hello Wörld!'.encode('utf-8')
+ assert resp.response == ['Hello ', u'Wörld!'.encode('utf-8')]
+ assert not resp.is_streamed
+ assert resp.is_sequence
+
+ # automatic generator sequence conversion
+ resp.response = generate_items()
+ resp.implicit_sequence_conversion = False
+ assert resp.is_streamed
+ assert not resp.is_sequence
+ self.assert_raises(RuntimeError, lambda: resp.data)
+ resp.make_sequence()
+ assert resp.data == u'Hello Wörld!'.encode('utf-8')
+ assert resp.response == ['Hello ', u'Wörld!'.encode('utf-8')]
+ assert not resp.is_streamed
+ assert resp.is_sequence
+
+ # stream makes it a list no matter how the conversion is set
+ for val in True, False:
+ resp.implicit_sequence_conversion = val
+ resp.response = ("foo", "bar")
+ assert resp.is_sequence
+ resp.stream.write('baz')
+ assert resp.response == ['foo', 'bar', 'baz']
+
+ def test_form_data_ordering(self):
+ class MyRequest(wrappers.Request):
+ parameter_storage_class = ImmutableOrderedMultiDict
+
+ req = MyRequest.from_values('/?foo=1&bar=0&foo=3')
+ assert list(req.args) == ['foo', 'bar']
+ assert req.args.items(multi=True) == [
+ ('foo', '1'),
+ ('bar', '0'),
+ ('foo', '3')
+ ]
+ assert isinstance(req.args, ImmutableOrderedMultiDict)
+ assert isinstance(req.values, CombinedMultiDict)
+ assert req.values['foo'] == '1'
+ assert req.values.getlist('foo') == ['1', '3']
+
+ def test_storage_classes(self):
+ class MyRequest(wrappers.Request):
+ dict_storage_class = dict
+ list_storage_class = list
+ parameter_storage_class = dict
+ req = MyRequest.from_values('/?foo=baz', headers={
+ 'Cookie': 'foo=bar'
+ })
+ assert type(req.cookies) is dict
+ assert req.cookies == {'foo': 'bar'}
+ assert type(req.access_route) is list
+
+ assert type(req.args) is dict
+ assert type(req.values) is CombinedMultiDict
+ assert req.values['foo'] == 'baz'
+
+ req = wrappers.Request.from_values(headers={
+ 'Cookie': 'foo=bar'
+ })
+ assert type(req.cookies) is ImmutableTypeConversionDict
+ assert req.cookies == {'foo': 'bar'}
+ assert type(req.access_route) is ImmutableList
+
+ MyRequest.list_storage_class = tuple
+ req = MyRequest.from_values()
+ assert type(req.access_route) is tuple
+
+ def test_response_headers_passthrough(self):
+ headers = wrappers.Headers()
+ resp = wrappers.Response(headers=headers)
+ assert resp.headers is headers
+
+ def test_response_304_no_content_length(self):
+ resp = wrappers.Response('Test', status=304)
+ env = create_environ()
+ assert 'content-length' not in resp.get_wsgi_headers(env)
+
+ def test_ranges(self):
+ # basic range stuff
+ req = wrappers.Request.from_values()
+ assert req.range is None
+ req = wrappers.Request.from_values(headers={'Range': 'bytes=0-499'})
+ assert req.range.ranges == [(0, 500)]
+
+ resp = wrappers.Response()
+ resp.content_range = req.range.make_content_range(1000)
+ assert resp.content_range.units == 'bytes'
+ assert resp.content_range.start == 0
+ assert resp.content_range.stop == 500
+ assert resp.content_range.length == 1000
+ assert resp.headers['Content-Range'] == 'bytes 0-499/1000'
+
+ resp.content_range.unset()
+ assert 'Content-Range' not in resp.headers
+
+ resp.headers['Content-Range'] = 'bytes 0-499/1000'
+ assert resp.content_range.units == 'bytes'
+ assert resp.content_range.start == 0
+ assert resp.content_range.stop == 500
+ assert resp.content_range.length == 1000
+
+ def test_auto_content_length(self):
+ resp = wrappers.Response('Hello World!')
+ assert resp.content_length == 12
+
+ resp = wrappers.Response(['Hello World!'])
+ assert resp.content_length is None
+ assert resp.get_wsgi_headers({})['Content-Length'] == '12'
+
+ def test_disabled_auto_content_length(self):
+ class MyResponse(wrappers.Response):
+ automatically_set_content_length = False
+ resp = MyResponse('Hello World!')
+ self.assert_(resp.content_length is None)
+
+ resp = MyResponse(['Hello World!'])
+ self.assert_(resp.content_length is None)
+ self.assert_('Content-Length' not in resp.get_wsgi_headers({}))
+
+ def test_location_header_autocorrect(self):
+ env = create_environ()
+ class MyResponse(wrappers.Response):
+ autocorrect_location_header = False
+ resp = MyResponse('Hello World!')
+ resp.headers['Location'] = '/test'
+ self.assert_equal(resp.get_wsgi_headers(env)['Location'], '/test')
+
+ resp = wrappers.Response('Hello World!')
+ resp.headers['Location'] = '/test'
+ self.assert_equal(resp.get_wsgi_headers(env)['Location'], 'http://localhost/test')
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(WrappersTestCase))
+ return suite
diff --git a/websdk/werkzeug/testsuite/wsgi.py b/websdk/werkzeug/testsuite/wsgi.py
new file mode 100644
index 0000000..6602fa2
--- /dev/null
+++ b/websdk/werkzeug/testsuite/wsgi.py
@@ -0,0 +1,217 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.testsuite.wsgi
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Tests the WSGI utilities.
+
+ :copyright: (c) 2011 by Armin Ronacher.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import with_statement
+
+import unittest
+from os import path
+from cStringIO import StringIO
+
+from werkzeug.testsuite import WerkzeugTestCase
+
+from werkzeug.wrappers import BaseResponse
+from werkzeug.exceptions import BadRequest, ClientDisconnected
+from werkzeug.test import Client, create_environ, run_wsgi_app
+from werkzeug import wsgi
+
+
+class WSGIUtilsTestCase(WerkzeugTestCase):
+
+ def test_shareddatamiddleware_get_file_loader(self):
+ app = wsgi.SharedDataMiddleware(None, {})
+ assert callable(app.get_file_loader('foo'))
+
+ def test_shared_data_middleware(self):
+ def null_application(environ, start_response):
+ start_response('404 NOT FOUND', [('Content-Type', 'text/plain')])
+ yield 'NOT FOUND'
+ app = wsgi.SharedDataMiddleware(null_application, {
+ '/': path.join(path.dirname(__file__), 'res'),
+ '/sources': path.join(path.dirname(__file__), 'res'),
+ '/pkg': ('werkzeug.debug', 'shared')
+ })
+
+ for p in '/test.txt', '/sources/test.txt':
+ app_iter, status, headers = run_wsgi_app(app, create_environ(p))
+ assert status == '200 OK'
+ assert ''.join(app_iter).strip() == 'FOUND'
+
+ app_iter, status, headers = run_wsgi_app(app, create_environ('/pkg/debugger.js'))
+ contents = ''.join(app_iter)
+ assert '$(function() {' in contents
+
+ app_iter, status, headers = run_wsgi_app(app, create_environ('/missing'))
+ assert status == '404 NOT FOUND'
+ assert ''.join(app_iter).strip() == 'NOT FOUND'
+
+ def test_get_host(self):
+ env = {'HTTP_X_FORWARDED_HOST': 'example.org',
+ 'SERVER_NAME': 'bullshit', 'HOST_NAME': 'ignore me dammit'}
+ assert wsgi.get_host(env) == 'example.org'
+ assert wsgi.get_host(create_environ('/', 'http://example.org')) \
+ == 'example.org'
+
+ def test_responder(self):
+ def foo(environ, start_response):
+ return BaseResponse('Test')
+ client = Client(wsgi.responder(foo), BaseResponse)
+ response = client.get('/')
+ assert response.status_code == 200
+ assert response.data == 'Test'
+
+ def test_pop_path_info(self):
+ original_env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b///c'}
+
+ # regular path info popping
+ def assert_tuple(script_name, path_info):
+ assert env.get('SCRIPT_NAME') == script_name
+ assert env.get('PATH_INFO') == path_info
+ env = original_env.copy()
+ pop = lambda: wsgi.pop_path_info(env)
+
+ assert_tuple('/foo', '/a/b///c')
+ assert pop() == 'a'
+ assert_tuple('/foo/a', '/b///c')
+ assert pop() == 'b'
+ assert_tuple('/foo/a/b', '///c')
+ assert pop() == 'c'
+ assert_tuple('/foo/a/b///c', '')
+ assert pop() is None
+
+ def test_peek_path_info(self):
+ env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/aaa/b///c'}
+
+ assert wsgi.peek_path_info(env) == 'aaa'
+ assert wsgi.peek_path_info(env) == 'aaa'
+
+ def test_limited_stream(self):
+ class RaisingLimitedStream(wsgi.LimitedStream):
+ def on_exhausted(self):
+ raise BadRequest('input stream exhausted')
+
+ io = StringIO('123456')
+ stream = RaisingLimitedStream(io, 3)
+ assert stream.read() == '123'
+ self.assert_raises(BadRequest, stream.read)
+
+ io = StringIO('123456')
+ stream = RaisingLimitedStream(io, 3)
+ assert stream.read(1) == '1'
+ assert stream.read(1) == '2'
+ assert stream.read(1) == '3'
+ self.assert_raises(BadRequest, stream.read)
+
+ io = StringIO('123456\nabcdefg')
+ stream = wsgi.LimitedStream(io, 9)
+ assert stream.readline() == '123456\n'
+ assert stream.readline() == 'ab'
+
+ io = StringIO('123456\nabcdefg')
+ stream = wsgi.LimitedStream(io, 9)
+ assert stream.readlines() == ['123456\n', 'ab']
+
+ io = StringIO('123456\nabcdefg')
+ stream = wsgi.LimitedStream(io, 9)
+ assert stream.readlines(2) == ['12']
+ assert stream.readlines(2) == ['34']
+ assert stream.readlines() == ['56\n', 'ab']
+
+ io = StringIO('123456\nabcdefg')
+ stream = wsgi.LimitedStream(io, 9)
+ assert stream.readline(100) == '123456\n'
+
+ io = StringIO('123456\nabcdefg')
+ stream = wsgi.LimitedStream(io, 9)
+ assert stream.readlines(100) == ['123456\n', 'ab']
+
+ io = StringIO('123456')
+ stream = wsgi.LimitedStream(io, 3)
+ assert stream.read(1) == '1'
+ assert stream.read(1) == '2'
+ assert stream.read() == '3'
+ assert stream.read() == ''
+
+ io = StringIO('123456')
+ stream = wsgi.LimitedStream(io, 3)
+ assert stream.read(-1) == '123'
+
+ def test_limited_stream_disconnection(self):
+ io = StringIO('A bit of content')
+
+ # disconnect detection on out of bytes
+ stream = wsgi.LimitedStream(io, 255)
+ with self.assert_raises(ClientDisconnected):
+ stream.read()
+
+ # disconnect detection because file close
+ io = StringIO('x' * 255)
+ io.close()
+ stream = wsgi.LimitedStream(io, 255)
+ with self.assert_raises(ClientDisconnected):
+ stream.read()
+
+ def test_path_info_extraction(self):
+ x = wsgi.extract_path_info('http://example.com/app', '/app/hello')
+ assert x == u'/hello'
+ x = wsgi.extract_path_info('http://example.com/app',
+ 'https://example.com/app/hello')
+ assert x == u'/hello'
+ x = wsgi.extract_path_info('http://example.com/app/',
+ 'https://example.com/app/hello')
+ assert x == u'/hello'
+ x = wsgi.extract_path_info('http://example.com/app/',
+ 'https://example.com/app')
+ assert x == u'/'
+ x = wsgi.extract_path_info(u'http://☃.net/', u'/fööbär')
+ assert x == u'/fööbär'
+ x = wsgi.extract_path_info(u'http://☃.net/x', u'http://☃.net/x/fööbär')
+ assert x == u'/fööbär'
+
+ env = create_environ(u'/fööbär', u'http://☃.net/x/')
+ x = wsgi.extract_path_info(env, u'http://☃.net/x/fööbär')
+ assert x == u'/fööbär'
+
+ x = wsgi.extract_path_info('http://example.com/app/',
+ 'https://example.com/a/hello')
+ assert x is None
+ x = wsgi.extract_path_info('http://example.com/app/',
+ 'https://example.com/app/hello',
+ collapse_http_schemes=False)
+ assert x is None
+
+ def test_get_host_fallback(self):
+ assert wsgi.get_host({
+ 'SERVER_NAME': 'foobar.example.com',
+ 'wsgi.url_scheme': 'http',
+ 'SERVER_PORT': '80'
+ }) == 'foobar.example.com'
+ assert wsgi.get_host({
+ 'SERVER_NAME': 'foobar.example.com',
+ 'wsgi.url_scheme': 'http',
+ 'SERVER_PORT': '81'
+ }) == 'foobar.example.com:81'
+
+ def test_multi_part_line_breaks(self):
+ data = 'abcdef\r\nghijkl\r\nmnopqrstuvwxyz\r\nABCDEFGHIJK'
+ test_stream = StringIO(data)
+ lines = list(wsgi.make_line_iter(test_stream, limit=len(data), buffer_size=16))
+ assert lines == ['abcdef\r\n', 'ghijkl\r\n', 'mnopqrstuvwxyz\r\n', 'ABCDEFGHIJK']
+
+ data = 'abc\r\nThis line is broken by the buffer length.\r\nFoo bar baz'
+ test_stream = StringIO(data)
+ lines = list(wsgi.make_line_iter(test_stream, limit=len(data), buffer_size=24))
+ assert lines == ['abc\r\n', 'This line is broken by the buffer length.\r\n', 'Foo bar baz']
+
+
+def suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(WSGIUtilsTestCase))
+ return suite
diff --git a/websdk/werkzeug/urls.py b/websdk/werkzeug/urls.py
new file mode 100644
index 0000000..267e8cd
--- /dev/null
+++ b/websdk/werkzeug/urls.py
@@ -0,0 +1,539 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.urls
+ ~~~~~~~~~~~~~
+
+ This module implements various URL related functions.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import urlparse
+
+from werkzeug._internal import _decode_unicode
+from werkzeug.datastructures import MultiDict, iter_multi_items
+from werkzeug.wsgi import make_chunk_iter
+
+
+#: list of characters that are always safe in URLs.
+_always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ 'abcdefghijklmnopqrstuvwxyz'
+ '0123456789_.-')
+_safe_map = dict((c, c) for c in _always_safe)
+for i in xrange(0x80):
+ c = chr(i)
+ if c not in _safe_map:
+ _safe_map[c] = '%%%02X' % i
+_safe_map.update((chr(i), '%%%02X' % i) for i in xrange(0x80, 0x100))
+_safemaps = {}
+
+#: lookup table for encoded characters.
+_hexdig = '0123456789ABCDEFabcdef'
+_hextochr = dict((a + b, chr(int(a + b, 16)))
+ for a in _hexdig for b in _hexdig)
+
+
+def _quote(s, safe='/', _join=''.join):
+ assert isinstance(s, str), 'quote only works on bytes'
+ if not s or not s.rstrip(_always_safe + safe):
+ return s
+ try:
+ quoter = _safemaps[safe]
+ except KeyError:
+ safe_map = _safe_map.copy()
+ safe_map.update([(c, c) for c in safe])
+ _safemaps[safe] = quoter = safe_map.__getitem__
+ return _join(map(quoter, s))
+
+
+def _quote_plus(s, safe=''):
+ if ' ' in s:
+ return _quote(s, safe + ' ').replace(' ', '+')
+ return _quote(s, safe)
+
+
+def _safe_urlsplit(s):
+ """the urlparse.urlsplit cache breaks if it contains unicode and
+ we cannot control that. So we force type cast that thing back
+ to what we think it is.
+ """
+ rv = urlparse.urlsplit(s)
+ # we have to check rv[2] here and not rv[1] as rv[1] will be
+ # an empty bytestring in case no domain was given.
+ if type(rv[2]) is not type(s):
+ assert hasattr(urlparse, 'clear_cache')
+ urlparse.clear_cache()
+ rv = urlparse.urlsplit(s)
+ assert type(rv[2]) is type(s)
+ return rv
+
+
+def _unquote(s, unsafe=''):
+ assert isinstance(s, str), 'unquote only works on bytes'
+ rv = s.split('%')
+ if len(rv) == 1:
+ return s
+ s = rv[0]
+ for item in rv[1:]:
+ try:
+ char = _hextochr[item[:2]]
+ if char in unsafe:
+ raise KeyError()
+ s += char + item[2:]
+ except KeyError:
+ s += '%' + item
+ return s
+
+
+def _unquote_plus(s):
+ return _unquote(s.replace('+', ' '))
+
+
+def _uri_split(uri):
+ """Splits up an URI or IRI."""
+ scheme, netloc, path, query, fragment = _safe_urlsplit(uri)
+
+ port = None
+
+ if '@' in netloc:
+ auth, hostname = netloc.split('@', 1)
+ else:
+ auth = None
+ hostname = netloc
+ if hostname:
+ if ':' in hostname:
+ hostname, port = hostname.split(':', 1)
+ return scheme, auth, hostname, port, path, query, fragment
+
+
+def iri_to_uri(iri, charset='utf-8'):
+ r"""Converts any unicode based IRI to an acceptable ASCII URI. Werkzeug
+ always uses utf-8 URLs internally because this is what browsers and HTTP
+ do as well. In some places where it accepts an URL it also accepts a
+ unicode IRI and converts it into a URI.
+
+ Examples for IRI versus URI:
+
+ >>> iri_to_uri(u'http://☃.net/')
+ 'http://xn--n3h.net/'
+ >>> iri_to_uri(u'http://üser:pässword@☃.net/påth')
+ 'http://%C3%BCser:p%C3%A4ssword@xn--n3h.net/p%C3%A5th'
+
+ .. versionadded:: 0.6
+
+ :param iri: the iri to convert
+ :param charset: the charset for the URI
+ """
+ iri = unicode(iri)
+ scheme, auth, hostname, port, path, query, fragment = _uri_split(iri)
+
+ scheme = scheme.encode('ascii')
+ hostname = hostname.encode('idna')
+ if auth:
+ if ':' in auth:
+ auth, password = auth.split(':', 1)
+ else:
+ password = None
+ auth = _quote(auth.encode(charset))
+ if password:
+ auth += ':' + _quote(password.encode(charset))
+ hostname = auth + '@' + hostname
+ if port:
+ hostname += ':' + port
+
+ path = _quote(path.encode(charset), safe="/:~+")
+ query = _quote(query.encode(charset), safe="=%&[]:;$()+,!?*/")
+
+ # this absolutely always must return a string. Otherwise some parts of
+ # the system might perform double quoting (#61)
+ return str(urlparse.urlunsplit([scheme, hostname, path, query, fragment]))
+
+
+def uri_to_iri(uri, charset='utf-8', errors='replace'):
+ r"""Converts a URI in a given charset to a IRI.
+
+ Examples for URI versus IRI
+
+ >>> uri_to_iri('http://xn--n3h.net/')
+ u'http://\u2603.net/'
+ >>> uri_to_iri('http://%C3%BCser:p%C3%A4ssword@xn--n3h.net/p%C3%A5th')
+ u'http://\xfcser:p\xe4ssword@\u2603.net/p\xe5th'
+
+ Query strings are left unchanged:
+
+ >>> uri_to_iri('/?foo=24&x=%26%2f')
+ u'/?foo=24&x=%26%2f'
+
+ .. versionadded:: 0.6
+
+ :param uri: the URI to convert
+ :param charset: the charset of the URI
+ :param errors: the error handling on decode
+ """
+ uri = url_fix(str(uri), charset)
+ scheme, auth, hostname, port, path, query, fragment = _uri_split(uri)
+
+ scheme = _decode_unicode(scheme, 'ascii', errors)
+
+ try:
+ hostname = hostname.decode('idna')
+ except UnicodeError:
+ # dammit, that codec raised an error. Because it does not support
+ # any error handling we have to fake it.... badly
+ if errors not in ('ignore', 'replace'):
+ raise
+ hostname = hostname.decode('ascii', errors)
+
+ if auth:
+ if ':' in auth:
+ auth, password = auth.split(':', 1)
+ else:
+ password = None
+ auth = _decode_unicode(_unquote(auth), charset, errors)
+ if password:
+ auth += u':' + _decode_unicode(_unquote(password),
+ charset, errors)
+ hostname = auth + u'@' + hostname
+ if port:
+ # port should be numeric, but you never know...
+ hostname += u':' + port.decode(charset, errors)
+
+ path = _decode_unicode(_unquote(path, '/;?'), charset, errors)
+ query = _decode_unicode(_unquote(query, ';/?:@&=+,$'),
+ charset, errors)
+
+ return urlparse.urlunsplit([scheme, hostname, path, query, fragment])
+
+
+def url_decode(s, charset='utf-8', decode_keys=False, include_empty=True,
+ errors='replace', separator='&', cls=None):
+ """Parse a querystring and return it as :class:`MultiDict`. Per default
+ only values are decoded into unicode strings. If `decode_keys` is set to
+ `True` the same will happen for keys.
+
+ Per default a missing value for a key will default to an empty key. If
+ you don't want that behavior you can set `include_empty` to `False`.
+
+ Per default encoding errors are ignored. If you want a different behavior
+ you can set `errors` to ``'replace'`` or ``'strict'``. In strict mode a
+ `HTTPUnicodeError` is raised.
+
+ .. versionchanged:: 0.5
+ In previous versions ";" and "&" could be used for url decoding.
+ This changed in 0.5 where only "&" is supported. If you want to
+ use ";" instead a different `separator` can be provided.
+
+ The `cls` parameter was added.
+
+ :param s: a string with the query string to decode.
+ :param charset: the charset of the query string.
+ :param decode_keys: set to `True` if you want the keys to be decoded
+ as well.
+ :param include_empty: Set to `False` if you don't want empty values to
+ appear in the dict.
+ :param errors: the decoding error behavior.
+ :param separator: the pair separator to be used, defaults to ``&``
+ :param cls: an optional dict class to use. If this is not specified
+ or `None` the default :class:`MultiDict` is used.
+ """
+ if cls is None:
+ cls = MultiDict
+ return cls(_url_decode_impl(str(s).split(separator), charset, decode_keys,
+ include_empty, errors))
+
+
+def url_decode_stream(stream, charset='utf-8', decode_keys=False,
+ include_empty=True, errors='replace', separator='&',
+ cls=None, limit=None, return_iterator=False):
+ """Works like :func:`url_decode` but decodes a stream. The behavior
+ of stream and limit follows functions like
+ :func:`~werkzeug.wsgi.make_line_iter`. The generator of pairs is
+ directly fed to the `cls` so you can consume the data while it's
+ parsed.
+
+ .. versionadded:: 0.8
+
+ :param stream: a stream with the encoded querystring
+ :param charset: the charset of the query string.
+ :param decode_keys: set to `True` if you want the keys to be decoded
+ as well.
+ :param include_empty: Set to `False` if you don't want empty values to
+ appear in the dict.
+ :param errors: the decoding error behavior.
+ :param separator: the pair separator to be used, defaults to ``&``
+ :param cls: an optional dict class to use. If this is not specified
+ or `None` the default :class:`MultiDict` is used.
+ :param limit: the content length of the URL data. Not necessary if
+ a limited stream is provided.
+ :param return_iterator: if set to `True` the `cls` argument is ignored
+ and an iterator over all decoded pairs is
+ returned
+ """
+ if return_iterator:
+ cls = lambda x: x
+ elif cls is None:
+ cls = MultiDict
+ pair_iter = make_chunk_iter(stream, separator, limit)
+ return cls(_url_decode_impl(pair_iter, charset, decode_keys,
+ include_empty, errors))
+
+
+def _url_decode_impl(pair_iter, charset, decode_keys, include_empty,
+ errors):
+ for pair in pair_iter:
+ if not pair:
+ continue
+ if '=' in pair:
+ key, value = pair.split('=', 1)
+ else:
+ if not include_empty:
+ continue
+ key = pair
+ value = ''
+ key = _unquote_plus(key)
+ if decode_keys:
+ key = _decode_unicode(key, charset, errors)
+ yield key, url_unquote_plus(value, charset, errors)
+
+
+def url_encode(obj, charset='utf-8', encode_keys=False, sort=False, key=None,
+ separator='&'):
+ """URL encode a dict/`MultiDict`. If a value is `None` it will not appear
+ in the result string. Per default only values are encoded into the target
+ charset strings. If `encode_keys` is set to ``True`` unicode keys are
+ supported too.
+
+ If `sort` is set to `True` the items are sorted by `key` or the default
+ sorting algorithm.
+
+ .. versionadded:: 0.5
+ `sort`, `key`, and `separator` were added.
+
+ :param obj: the object to encode into a query string.
+ :param charset: the charset of the query string.
+ :param encode_keys: set to `True` if you have unicode keys.
+ :param sort: set to `True` if you want parameters to be sorted by `key`.
+ :param separator: the separator to be used for the pairs.
+ :param key: an optional function to be used for sorting. For more details
+ check out the :func:`sorted` documentation.
+ """
+ return separator.join(_url_encode_impl(obj, charset, encode_keys, sort, key))
+
+
+def url_encode_stream(obj, stream=None, charset='utf-8', encode_keys=False,
+ sort=False, key=None, separator='&'):
+ """Like :meth:`url_encode` but writes the results to a stream
+ object. If the stream is `None` a generator over all encoded
+ pairs is returned.
+
+ .. versionadded:: 0.8
+
+ :param obj: the object to encode into a query string.
+ :param stream: a stream to write the encoded object into or `None` if
+ an iterator over the encoded pairs should be returned. In
+ that case the separator argument is ignored.
+ :param charset: the charset of the query string.
+ :param encode_keys: set to `True` if you have unicode keys.
+ :param sort: set to `True` if you want parameters to be sorted by `key`.
+ :param separator: the separator to be used for the pairs.
+ :param key: an optional function to be used for sorting. For more details
+ check out the :func:`sorted` documentation.
+ """
+ gen = _url_encode_impl(obj, charset, encode_keys, sort, key)
+ if stream is None:
+ return gen
+ for idx, chunk in enumerate(gen):
+ if idx:
+ stream.write(separator)
+ stream.write(chunk)
+
+
+def _url_encode_impl(obj, charset, encode_keys, sort, key):
+ iterable = iter_multi_items(obj)
+ if sort:
+ iterable = sorted(iterable, key=key)
+ for key, value in iterable:
+ if value is None:
+ continue
+ if encode_keys and isinstance(key, unicode):
+ key = key.encode(charset)
+ else:
+ key = str(key)
+ if isinstance(value, unicode):
+ value = value.encode(charset)
+ else:
+ value = str(value)
+ yield '%s=%s' % (_quote(key), _quote_plus(value))
+
+
+def url_quote(s, charset='utf-8', safe='/:'):
+ """URL encode a single string with a given encoding.
+
+ :param s: the string to quote.
+ :param charset: the charset to be used.
+ :param safe: an optional sequence of safe characters.
+ """
+ if isinstance(s, unicode):
+ s = s.encode(charset)
+ elif not isinstance(s, str):
+ s = str(s)
+ return _quote(s, safe=safe)
+
+
+def url_quote_plus(s, charset='utf-8', safe=''):
+ """URL encode a single string with the given encoding and convert
+ whitespace to "+".
+
+ :param s: the string to quote.
+ :param charset: the charset to be used.
+ :param safe: an optional sequence of safe characters.
+ """
+ if isinstance(s, unicode):
+ s = s.encode(charset)
+ elif not isinstance(s, str):
+ s = str(s)
+ return _quote_plus(s, safe=safe)
+
+
+def url_unquote(s, charset='utf-8', errors='replace'):
+ """URL decode a single string with a given decoding.
+
+ Per default encoding errors are ignored. If you want a different behavior
+ you can set `errors` to ``'replace'`` or ``'strict'``. In strict mode a
+ `HTTPUnicodeError` is raised.
+
+ :param s: the string to unquote.
+ :param charset: the charset to be used.
+ :param errors: the error handling for the charset decoding.
+ """
+ if isinstance(s, unicode):
+ s = s.encode(charset)
+ return _decode_unicode(_unquote(s), charset, errors)
+
+
+def url_unquote_plus(s, charset='utf-8', errors='replace'):
+ """URL decode a single string with the given decoding and decode
+ a "+" to whitespace.
+
+ Per default encoding errors are ignored. If you want a different behavior
+ you can set `errors` to ``'replace'`` or ``'strict'``. In strict mode a
+ `HTTPUnicodeError` is raised.
+
+ :param s: the string to unquote.
+ :param charset: the charset to be used.
+ :param errors: the error handling for the charset decoding.
+ """
+ if isinstance(s, unicode):
+ s = s.encode(charset)
+ return _decode_unicode(_unquote_plus(s), charset, errors)
+
+
+def url_fix(s, charset='utf-8'):
+ r"""Sometimes you get an URL by a user that just isn't a real URL because
+ it contains unsafe characters like ' ' and so on. This function can fix
+ some of the problems in a similar way browsers handle data entered by the
+ user:
+
+ >>> url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffskl\xe4rung)')
+ 'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'
+
+ :param s: the string with the URL to fix.
+ :param charset: The target charset for the URL if the url was given as
+ unicode string.
+ """
+ if isinstance(s, unicode):
+ s = s.encode(charset, 'replace')
+ scheme, netloc, path, qs, anchor = _safe_urlsplit(s)
+ path = _quote(path, '/%')
+ qs = _quote_plus(qs, ':&%=')
+ return urlparse.urlunsplit((scheme, netloc, path, qs, anchor))
+
+
+class Href(object):
+ """Implements a callable that constructs URLs with the given base. The
+ function can be called with any number of positional and keyword
+ arguments which than are used to assemble the URL. Works with URLs
+ and posix paths.
+
+ Positional arguments are appended as individual segments to
+ the path of the URL:
+
+ >>> href = Href('/foo')
+ >>> href('bar', 23)
+ '/foo/bar/23'
+ >>> href('foo', bar=23)
+ '/foo/foo?bar=23'
+
+ If any of the arguments (positional or keyword) evaluates to `None` it
+ will be skipped. If no keyword arguments are given the last argument
+ can be a :class:`dict` or :class:`MultiDict` (or any other dict subclass),
+ otherwise the keyword arguments are used for the query parameters, cutting
+ off the first trailing underscore of the parameter name:
+
+ >>> href(is_=42)
+ '/foo?is=42'
+ >>> href({'foo': 'bar'})
+ '/foo?foo=bar'
+
+ Combining of both methods is not allowed:
+
+ >>> href({'foo': 'bar'}, bar=42)
+ Traceback (most recent call last):
+ ...
+ TypeError: keyword arguments and query-dicts can't be combined
+
+ Accessing attributes on the href object creates a new href object with
+ the attribute name as prefix:
+
+ >>> bar_href = href.bar
+ >>> bar_href("blub")
+ '/foo/bar/blub'
+
+ If `sort` is set to `True` the items are sorted by `key` or the default
+ sorting algorithm:
+
+ >>> href = Href("/", sort=True)
+ >>> href(a=1, b=2, c=3)
+ '/?a=1&b=2&c=3'
+
+ .. versionadded:: 0.5
+ `sort` and `key` were added.
+ """
+
+ def __init__(self, base='./', charset='utf-8', sort=False, key=None):
+ if not base:
+ base = './'
+ self.base = base
+ self.charset = charset
+ self.sort = sort
+ self.key = key
+
+ def __getattr__(self, name):
+ if name[:2] == '__':
+ raise AttributeError(name)
+ base = self.base
+ if base[-1:] != '/':
+ base += '/'
+ return Href(urlparse.urljoin(base, name), self.charset, self.sort,
+ self.key)
+
+ def __call__(self, *path, **query):
+ if path and isinstance(path[-1], dict):
+ if query:
+ raise TypeError('keyword arguments and query-dicts '
+ 'can\'t be combined')
+ query, path = path[-1], path[:-1]
+ elif query:
+ query = dict([(k.endswith('_') and k[:-1] or k, v)
+ for k, v in query.items()])
+ path = '/'.join([url_quote(x, self.charset) for x in path
+ if x is not None]).lstrip('/')
+ rv = self.base
+ if path:
+ if not rv.endswith('/'):
+ rv += '/'
+ rv = urlparse.urljoin(rv, './' + path)
+ if query:
+ rv += '?' + url_encode(query, self.charset, sort=self.sort,
+ key=self.key)
+ return str(rv)
diff --git a/websdk/werkzeug/useragents.py b/websdk/werkzeug/useragents.py
new file mode 100644
index 0000000..fd70b26
--- /dev/null
+++ b/websdk/werkzeug/useragents.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.useragents
+ ~~~~~~~~~~~~~~~~~~~
+
+ This module provides a helper to inspect user agent strings. This module
+ is far from complete but should work for most of the currently available
+ browsers.
+
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+
+
+class UserAgentParser(object):
+ """A simple user agent parser. Used by the `UserAgent`."""
+
+ platforms = (
+ ('iphone|ios', 'iphone'),
+ (r'darwin|mac|os\s*x', 'macos'),
+ ('win', 'windows'),
+ (r'android', 'android'),
+ (r'x11|lin(\b|ux)?', 'linux'),
+ ('(sun|i86)os', 'solaris'),
+ (r'nintendo\s+wii', 'wii'),
+ ('irix', 'irix'),
+ ('hp-?ux', 'hpux'),
+ ('aix', 'aix'),
+ ('sco|unix_sv', 'sco'),
+ ('bsd', 'bsd'),
+ ('amiga', 'amiga')
+ )
+ browsers = (
+ ('googlebot', 'google'),
+ ('msnbot', 'msn'),
+ ('yahoo', 'yahoo'),
+ ('ask jeeves', 'ask'),
+ (r'aol|america\s+online\s+browser', 'aol'),
+ ('opera', 'opera'),
+ ('chrome', 'chrome'),
+ ('firefox|firebird|phoenix|iceweasel', 'firefox'),
+ ('galeon', 'galeon'),
+ ('safari', 'safari'),
+ ('webkit', 'webkit'),
+ ('camino', 'camino'),
+ ('konqueror', 'konqueror'),
+ ('k-meleon', 'kmeleon'),
+ ('netscape', 'netscape'),
+ (r'msie|microsoft\s+internet\s+explorer', 'msie'),
+ ('lynx', 'lynx'),
+ ('links', 'links'),
+ ('seamonkey|mozilla', 'seamonkey')
+ )
+
+ _browser_version_re = r'(?:%s)[/\sa-z(]*(\d+[.\da-z]+)?(?i)'
+ _language_re = re.compile(
+ r'(?:;\s*|\s+)(\b\w{2}\b(?:-\b\w{2}\b)?)\s*;|'
+ r'(?:\(|\[|;)\s*(\b\w{2}\b(?:-\b\w{2}\b)?)\s*(?:\]|\)|;)'
+ )
+
+ def __init__(self):
+ self.platforms = [(b, re.compile(a, re.I)) for a, b in self.platforms]
+ self.browsers = [(b, re.compile(self._browser_version_re % a))
+ for a, b in self.browsers]
+
+ def __call__(self, user_agent):
+ for platform, regex in self.platforms:
+ match = regex.search(user_agent)
+ if match is not None:
+ break
+ else:
+ platform = None
+ for browser, regex in self.browsers:
+ match = regex.search(user_agent)
+ if match is not None:
+ version = match.group(1)
+ break
+ else:
+ browser = version = None
+ match = self._language_re.search(user_agent)
+ if match is not None:
+ language = match.group(1) or match.group(2)
+ else:
+ language = None
+ return platform, browser, version, language
+
+
+class UserAgent(object):
+ """Represents a user agent. Pass it a WSGI environment or a user agent
+ string and you can inspect some of the details from the user agent
+ string via the attributes. The following attributes exist:
+
+ .. attribute:: string
+
+ the raw user agent string
+
+ .. attribute:: platform
+
+ the browser platform. The following platforms are currently
+ recognized:
+
+ - `aix`
+ - `amiga`
+ - `android`
+ - `bsd`
+ - `hpux`
+ - `iphone`
+ - `irix`
+ - `linux`
+ - `macos`
+ - `sco`
+ - `solaris`
+ - `wii`
+ - `windows`
+
+ .. attribute:: browser
+
+ the name of the browser. The following browsers are currently
+ recognized:
+
+ - `aol` *
+ - `ask` *
+ - `camino`
+ - `chrome`
+ - `firefox`
+ - `galeon`
+ - `google` *
+ - `kmeleon`
+ - `konqueror`
+ - `links`
+ - `lynx`
+ - `msie`
+ - `msn`
+ - `netscape`
+ - `opera`
+ - `safari`
+ - `seamonkey`
+ - `webkit`
+ - `yahoo` *
+
+ (Browsers maked with a star (``*``) are crawlers.)
+
+ .. attribute:: version
+
+ the version of the browser
+
+ .. attribute:: language
+
+ the language of the browser
+ """
+
+ _parser = UserAgentParser()
+
+ def __init__(self, environ_or_string):
+ if isinstance(environ_or_string, dict):
+ environ_or_string = environ_or_string.get('HTTP_USER_AGENT', '')
+ self.string = environ_or_string
+ self.platform, self.browser, self.version, self.language = \
+ self._parser(environ_or_string)
+
+ def to_header(self):
+ return self.string
+
+ def __str__(self):
+ return self.string
+
+ def __nonzero__(self):
+ return bool(self.browser)
+
+ def __repr__(self):
+ return '<%s %r/%s>' % (
+ self.__class__.__name__,
+ self.browser,
+ self.version
+ )
+
+
+# conceptionally this belongs in this module but because we want to lazily
+# load the user agent module (which happens in wrappers.py) we have to import
+# it afterwards. The class itself has the module set to this module so
+# pickle, inspect and similar modules treat the object as if it was really
+# implemented here.
+from werkzeug.wrappers import UserAgentMixin
diff --git a/websdk/werkzeug/utils.py b/websdk/werkzeug/utils.py
new file mode 100644
index 0000000..0cb04e0
--- /dev/null
+++ b/websdk/werkzeug/utils.py
@@ -0,0 +1,613 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.utils
+ ~~~~~~~~~~~~~~
+
+ This module implements various utilities for WSGI applications. Most of
+ them are used by the request and response wrappers but especially for
+ middleware development it makes sense to use them without the wrappers.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+import os
+import sys
+
+from werkzeug._internal import _iter_modules, _DictAccessorProperty, \
+ _parse_signature, _missing
+
+
+_format_re = re.compile(r'\$(?:(%s)|\{(%s)\})' % (('[a-zA-Z_][a-zA-Z0-9_]*',) * 2))
+_entity_re = re.compile(r'&([^;]+);')
+_filename_ascii_strip_re = re.compile(r'[^A-Za-z0-9_.-]')
+_windows_device_files = ('CON', 'AUX', 'COM1', 'COM2', 'COM3', 'COM4', 'LPT1',
+ 'LPT2', 'LPT3', 'PRN', 'NUL')
+
+
+class cached_property(object):
+ """A decorator that converts a function into a lazy property. The
+ function wrapped is called the first time to retrieve the result
+ and then that calculated result is used the next time you access
+ the value::
+
+ class Foo(object):
+
+ @cached_property
+ def foo(self):
+ # calculate something important here
+ return 42
+
+ The class has to have a `__dict__` in order for this property to
+ work.
+
+ .. versionchanged:: 0.6
+ the `writeable` attribute and parameter was deprecated. If a
+ cached property is writeable or not has to be documented now.
+ For performance reasons the implementation does not honor the
+ writeable setting and will always make the property writeable.
+ """
+
+ # implementation detail: this property is implemented as non-data
+ # descriptor. non-data descriptors are only invoked if there is
+ # no entry with the same name in the instance's __dict__.
+ # this allows us to completely get rid of the access function call
+ # overhead. If one choses to invoke __get__ by hand the property
+ # will still work as expected because the lookup logic is replicated
+ # in __get__ for manual invocation.
+
+ def __init__(self, func, name=None, doc=None, writeable=False):
+ if writeable:
+ from warnings import warn
+ warn(DeprecationWarning('the writeable argument to the '
+ 'cached property is a noop since 0.6 '
+ 'because the property is writeable '
+ 'by default for performance reasons'))
+
+ self.__name__ = name or func.__name__
+ self.__module__ = func.__module__
+ self.__doc__ = doc or func.__doc__
+ self.func = func
+
+ def __get__(self, obj, type=None):
+ if obj is None:
+ return self
+ value = obj.__dict__.get(self.__name__, _missing)
+ if value is _missing:
+ value = self.func(obj)
+ obj.__dict__[self.__name__] = value
+ return value
+
+
+class environ_property(_DictAccessorProperty):
+ """Maps request attributes to environment variables. This works not only
+ for the Werzeug request object, but also any other class with an
+ environ attribute:
+
+ >>> class Test(object):
+ ... environ = {'key': 'value'}
+ ... test = environ_property('key')
+ >>> var = Test()
+ >>> var.test
+ 'value'
+
+ If you pass it a second value it's used as default if the key does not
+ exist, the third one can be a converter that takes a value and converts
+ it. If it raises :exc:`ValueError` or :exc:`TypeError` the default value
+ is used. If no default value is provided `None` is used.
+
+ Per default the property is read only. You have to explicitly enable it
+ by passing ``read_only=False`` to the constructor.
+ """
+
+ read_only = True
+
+ def lookup(self, obj):
+ return obj.environ
+
+
+class header_property(_DictAccessorProperty):
+ """Like `environ_property` but for headers."""
+
+ def lookup(self, obj):
+ return obj.headers
+
+
+class HTMLBuilder(object):
+ """Helper object for HTML generation.
+
+ Per default there are two instances of that class. The `html` one, and
+ the `xhtml` one for those two dialects. The class uses keyword parameters
+ and positional parameters to generate small snippets of HTML.
+
+ Keyword parameters are converted to XML/SGML attributes, positional
+ arguments are used as children. Because Python accepts positional
+ arguments before keyword arguments it's a good idea to use a list with the
+ star-syntax for some children:
+
+ >>> html.p(class_='foo', *[html.a('foo', href='foo.html'), ' ',
+ ... html.a('bar', href='bar.html')])
+ u'<p class="foo"><a href="foo.html">foo</a> <a href="bar.html">bar</a></p>'
+
+ This class works around some browser limitations and can not be used for
+ arbitrary SGML/XML generation. For that purpose lxml and similar
+ libraries exist.
+
+ Calling the builder escapes the string passed:
+
+ >>> html.p(html("<foo>"))
+ u'<p>&lt;foo&gt;</p>'
+ """
+
+ from htmlentitydefs import name2codepoint
+ _entity_re = re.compile(r'&([^;]+);')
+ _entities = name2codepoint.copy()
+ _entities['apos'] = 39
+ _empty_elements = set([
+ 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame',
+ 'hr', 'img', 'input', 'keygen', 'isindex', 'link', 'meta', 'param',
+ 'source', 'wbr'
+ ])
+ _boolean_attributes = set([
+ 'selected', 'checked', 'compact', 'declare', 'defer', 'disabled',
+ 'ismap', 'multiple', 'nohref', 'noresize', 'noshade', 'nowrap'
+ ])
+ _plaintext_elements = set(['textarea'])
+ _c_like_cdata = set(['script', 'style'])
+ del name2codepoint
+
+ def __init__(self, dialect):
+ self._dialect = dialect
+
+ def __call__(self, s):
+ return escape(s)
+
+ def __getattr__(self, tag):
+ if tag[:2] == '__':
+ raise AttributeError(tag)
+ def proxy(*children, **arguments):
+ buffer = '<' + tag
+ for key, value in arguments.iteritems():
+ if value is None:
+ continue
+ if key[-1] == '_':
+ key = key[:-1]
+ if key in self._boolean_attributes:
+ if not value:
+ continue
+ if self._dialect == 'xhtml':
+ value = '="' + key + '"'
+ else:
+ value = ''
+ else:
+ value = '="' + escape(value, True) + '"'
+ buffer += ' ' + key + value
+ if not children and tag in self._empty_elements:
+ if self._dialect == 'xhtml':
+ buffer += ' />'
+ else:
+ buffer += '>'
+ return buffer
+ buffer += '>'
+
+ children_as_string = ''.join([unicode(x) for x in children
+ if x is not None])
+
+ if children_as_string:
+ if tag in self._plaintext_elements:
+ children_as_string = escape(children_as_string)
+ elif tag in self._c_like_cdata and self._dialect == 'xhtml':
+ children_as_string = '/*<![CDATA[*/' + \
+ children_as_string + '/*]]>*/'
+ buffer += children_as_string + '</' + tag + '>'
+ return buffer
+ return proxy
+
+ def __repr__(self):
+ return '<%s for %r>' % (
+ self.__class__.__name__,
+ self._dialect
+ )
+
+
+html = HTMLBuilder('html')
+xhtml = HTMLBuilder('xhtml')
+
+
+def get_content_type(mimetype, charset):
+ """Return the full content type string with charset for a mimetype.
+
+ If the mimetype represents text the charset will be appended as charset
+ parameter, otherwise the mimetype is returned unchanged.
+
+ :param mimetype: the mimetype to be used as content type.
+ :param charset: the charset to be appended in case it was a text mimetype.
+ :return: the content type.
+ """
+ if mimetype.startswith('text/') or \
+ mimetype == 'application/xml' or \
+ (mimetype.startswith('application/') and
+ mimetype.endswith('+xml')):
+ mimetype += '; charset=' + charset
+ return mimetype
+
+
+def format_string(string, context):
+ """String-template format a string:
+
+ >>> format_string('$foo and ${foo}s', dict(foo=42))
+ '42 and 42s'
+
+ This does not do any attribute lookup etc. For more advanced string
+ formattings have a look at the `werkzeug.template` module.
+
+ :param string: the format string.
+ :param context: a dict with the variables to insert.
+ """
+ def lookup_arg(match):
+ x = context[match.group(1) or match.group(2)]
+ if not isinstance(x, basestring):
+ x = type(string)(x)
+ return x
+ return _format_re.sub(lookup_arg, string)
+
+
+def secure_filename(filename):
+ r"""Pass it a filename and it will return a secure version of it. This
+ filename can then safely be stored on a regular file system and passed
+ to :func:`os.path.join`. The filename returned is an ASCII only string
+ for maximum portability.
+
+ On windows system the function also makes sure that the file is not
+ named after one of the special device files.
+
+ >>> secure_filename("My cool movie.mov")
+ 'My_cool_movie.mov'
+ >>> secure_filename("../../../etc/passwd")
+ 'etc_passwd'
+ >>> secure_filename(u'i contain cool \xfcml\xe4uts.txt')
+ 'i_contain_cool_umlauts.txt'
+
+ The function might return an empty filename. It's your responsibility
+ to ensure that the filename is unique and that you generate random
+ filename if the function returned an empty one.
+
+ .. versionadded:: 0.5
+
+ :param filename: the filename to secure
+ """
+ if isinstance(filename, unicode):
+ from unicodedata import normalize
+ filename = normalize('NFKD', filename).encode('ascii', 'ignore')
+ for sep in os.path.sep, os.path.altsep:
+ if sep:
+ filename = filename.replace(sep, ' ')
+ filename = str(_filename_ascii_strip_re.sub('', '_'.join(
+ filename.split()))).strip('._')
+
+ # on nt a couple of special files are present in each folder. We
+ # have to ensure that the target file is not such a filename. In
+ # this case we prepend an underline
+ if os.name == 'nt' and filename and \
+ filename.split('.')[0].upper() in _windows_device_files:
+ filename = '_' + filename
+
+ return filename
+
+
+def escape(s, quote=False):
+ """Replace special characters "&", "<" and ">" to HTML-safe sequences. If
+ the optional flag `quote` is `True`, the quotation mark character (") is
+ also translated.
+
+ There is a special handling for `None` which escapes to an empty string.
+
+ :param s: the string to escape.
+ :param quote: set to true to also escape double quotes.
+ """
+ if s is None:
+ return ''
+ elif hasattr(s, '__html__'):
+ return s.__html__()
+ elif not isinstance(s, basestring):
+ s = unicode(s)
+ s = s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
+ if quote:
+ s = s.replace('"', "&quot;")
+ return s
+
+
+def unescape(s):
+ """The reverse function of `escape`. This unescapes all the HTML
+ entities, not only the XML entities inserted by `escape`.
+
+ :param s: the string to unescape.
+ """
+ def handle_match(m):
+ name = m.group(1)
+ if name in HTMLBuilder._entities:
+ return unichr(HTMLBuilder._entities[name])
+ try:
+ if name[:2] in ('#x', '#X'):
+ return unichr(int(name[2:], 16))
+ elif name.startswith('#'):
+ return unichr(int(name[1:]))
+ except ValueError:
+ pass
+ return u''
+ return _entity_re.sub(handle_match, s)
+
+
+def redirect(location, code=302):
+ """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's not a real
+ redirect and 304 because it's the answer for a request with a request
+ with defined If-Modified-Since headers.
+
+ .. versionadded:: 0.6
+ The location can now be a unicode string that is encoded using
+ the :func:`iri_to_uri` function.
+
+ :param location: the location the response should redirect to.
+ :param code: the redirect status code. defaults to 302.
+ """
+ from werkzeug.wrappers import BaseResponse
+ display_location = location
+ if isinstance(location, unicode):
+ from werkzeug.urls import iri_to_uri
+ location = iri_to_uri(location)
+ response = BaseResponse(
+ '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
+ '<title>Redirecting...</title>\n'
+ '<h1>Redirecting...</h1>\n'
+ '<p>You should be redirected automatically to target URL: '
+ '<a href="%s">%s</a>. If not click the link.' %
+ (location, display_location), code, mimetype='text/html')
+ response.headers['Location'] = location
+ return response
+
+
+def append_slash_redirect(environ, code=301):
+ """Redirect to the same URL but with a slash appended. The behavior
+ of this function is undefined if the path ends with a slash already.
+
+ :param environ: the WSGI environment for the request that triggers
+ the redirect.
+ :param code: the status code for the redirect.
+ """
+ new_path = environ['PATH_INFO'].strip('/') + '/'
+ query_string = environ.get('QUERY_STRING')
+ if query_string:
+ new_path += '?' + query_string
+ return redirect(new_path, code)
+
+
+def import_string(import_name, silent=False):
+ """Imports an object based on a string. This is useful if you want to
+ use import paths as endpoints or something similar. An import path can
+ be specified either in dotted notation (``xml.sax.saxutils.escape``)
+ or with a colon as object delimiter (``xml.sax.saxutils:escape``).
+
+ If `silent` is True the return value will be `None` if the import fails.
+
+ For better debugging we recommend the new :func:`import_module`
+ function to be used instead.
+
+ :param import_name: the dotted name for the object to import.
+ :param silent: if set to `True` import errors are ignored and
+ `None` is returned instead.
+ :return: imported object
+ """
+ # force the import name to automatically convert to strings
+ if isinstance(import_name, unicode):
+ import_name = str(import_name)
+ try:
+ if ':' in import_name:
+ module, obj = import_name.split(':', 1)
+ elif '.' in import_name:
+ module, obj = import_name.rsplit('.', 1)
+ else:
+ return __import__(import_name)
+ # __import__ is not able to handle unicode strings in the fromlist
+ # if the module is a package
+ if isinstance(obj, unicode):
+ obj = obj.encode('utf-8')
+ try:
+ return getattr(__import__(module, None, None, [obj]), obj)
+ except (ImportError, AttributeError):
+ # support importing modules not yet set up by the parent module
+ # (or package for that matter)
+ modname = module + '.' + obj
+ __import__(modname)
+ return sys.modules[modname]
+ except ImportError, e:
+ if not silent:
+ raise ImportStringError(import_name, e), None, sys.exc_info()[2]
+
+
+def find_modules(import_path, include_packages=False, recursive=False):
+ """Find all the modules below a package. This can be useful to
+ automatically import all views / controllers so that their metaclasses /
+ function decorators have a chance to register themselves on the
+ application.
+
+ Packages are not returned unless `include_packages` is `True`. This can
+ also recursively list modules but in that case it will import all the
+ packages to get the correct load path of that module.
+
+ :param import_name: the dotted name for the package to find child modules.
+ :param include_packages: set to `True` if packages should be returned, too.
+ :param recursive: set to `True` if recursion should happen.
+ :return: generator
+ """
+ module = import_string(import_path)
+ path = getattr(module, '__path__', None)
+ if path is None:
+ raise ValueError('%r is not a package' % import_path)
+ basename = module.__name__ + '.'
+ for modname, ispkg in _iter_modules(path):
+ modname = basename + modname
+ if ispkg:
+ if include_packages:
+ yield modname
+ if recursive:
+ for item in find_modules(modname, include_packages, True):
+ yield item
+ else:
+ yield modname
+
+
+def validate_arguments(func, args, kwargs, drop_extra=True):
+ """Check if the function accepts the arguments and keyword arguments.
+ Returns a new ``(args, kwargs)`` tuple that can safely be passed to
+ the function without causing a `TypeError` because the function signature
+ is incompatible. If `drop_extra` is set to `True` (which is the default)
+ any extra positional or keyword arguments are dropped automatically.
+
+ The exception raised provides three attributes:
+
+ `missing`
+ A set of argument names that the function expected but where
+ missing.
+
+ `extra`
+ A dict of keyword arguments that the function can not handle but
+ where provided.
+
+ `extra_positional`
+ A list of values that where given by positional argument but the
+ function cannot accept.
+
+ This can be useful for decorators that forward user submitted data to
+ a view function::
+
+ from werkzeug.utils import ArgumentValidationError, validate_arguments
+
+ def sanitize(f):
+ def proxy(request):
+ data = request.values.to_dict()
+ try:
+ args, kwargs = validate_arguments(f, (request,), data)
+ except ArgumentValidationError:
+ raise BadRequest('The browser failed to transmit all '
+ 'the data expected.')
+ return f(*args, **kwargs)
+ return proxy
+
+ :param func: the function the validation is performed against.
+ :param args: a tuple of positional arguments.
+ :param kwargs: a dict of keyword arguments.
+ :param drop_extra: set to `False` if you don't want extra arguments
+ to be silently dropped.
+ :return: tuple in the form ``(args, kwargs)``.
+ """
+ parser = _parse_signature(func)
+ args, kwargs, missing, extra, extra_positional = parser(args, kwargs)[:5]
+ if missing:
+ raise ArgumentValidationError(tuple(missing))
+ elif (extra or extra_positional) and not drop_extra:
+ raise ArgumentValidationError(None, extra, extra_positional)
+ return tuple(args), kwargs
+
+
+def bind_arguments(func, args, kwargs):
+ """Bind the arguments provided into a dict. When passed a function,
+ a tuple of arguments and a dict of keyword arguments `bind_arguments`
+ returns a dict of names as the function would see it. This can be useful
+ to implement a cache decorator that uses the function arguments to build
+ the cache key based on the values of the arguments.
+
+ :param func: the function the arguments should be bound for.
+ :param args: tuple of positional arguments.
+ :param kwargs: a dict of keyword arguments.
+ :return: a :class:`dict` of bound keyword arguments.
+ """
+ args, kwargs, missing, extra, extra_positional, \
+ arg_spec, vararg_var, kwarg_var = _parse_signature(func)(args, kwargs)
+ values = {}
+ for (name, has_default, default), value in zip(arg_spec, args):
+ values[name] = value
+ if vararg_var is not None:
+ values[vararg_var] = tuple(extra_positional)
+ elif extra_positional:
+ raise TypeError('too many positional arguments')
+ if kwarg_var is not None:
+ multikw = set(extra) & set([x[0] for x in arg_spec])
+ if multikw:
+ raise TypeError('got multiple values for keyword argument ' +
+ repr(iter(multikw).next()))
+ values[kwarg_var] = extra
+ elif extra:
+ raise TypeError('got unexpected keyword argument ' +
+ repr(iter(extra).next()))
+ return values
+
+
+class ArgumentValidationError(ValueError):
+ """Raised if :func:`validate_arguments` fails to validate"""
+
+ def __init__(self, missing=None, extra=None, extra_positional=None):
+ self.missing = set(missing or ())
+ self.extra = extra or {}
+ self.extra_positional = extra_positional or []
+ ValueError.__init__(self, 'function arguments invalid. ('
+ '%d missing, %d additional)' % (
+ len(self.missing),
+ len(self.extra) + len(self.extra_positional)
+ ))
+
+
+class ImportStringError(ImportError):
+ """Provides information about a failed :func:`import_string` attempt."""
+
+ #: String in dotted notation that failed to be imported.
+ import_name = None
+ #: Wrapped exception.
+ exception = None
+
+ def __init__(self, import_name, exception):
+ self.import_name = import_name
+ self.exception = exception
+
+ msg = (
+ 'import_string() failed for %r. Possible reasons are:\n\n'
+ '- missing __init__.py in a package;\n'
+ '- package or module path not included in sys.path;\n'
+ '- duplicated package or module name taking precedence in '
+ 'sys.path;\n'
+ '- missing module, class, function or variable;\n\n'
+ 'Debugged import:\n\n%s\n\n'
+ 'Original exception:\n\n%s: %s')
+
+ name = ''
+ tracked = []
+ for part in import_name.replace(':', '.').split('.'):
+ name += (name and '.') + part
+ imported = import_string(name, silent=True)
+ if imported:
+ tracked.append((name, imported.__file__))
+ else:
+ track = ['- %r found in %r.' % (n, i) for n, i in tracked]
+ track.append('- %r not found.' % name)
+ msg = msg % (import_name, '\n'.join(track),
+ exception.__class__.__name__, str(exception))
+ break
+
+ ImportError.__init__(self, msg)
+
+ def __repr__(self):
+ return '<%s(%r, %r)>' % (self.__class__.__name__, self.import_name,
+ self.exception)
+
+
+# circular dependencies
+from werkzeug.http import quote_header_value, unquote_header_value, \
+ cookie_date
+
+# DEPRECATED
+# these objects were previously in this module as well. we import
+# them here for backwards compatibility with old pickles.
+from werkzeug.datastructures import MultiDict, CombinedMultiDict, \
+ Headers, EnvironHeaders
+from werkzeug.http import parse_cookie, dump_cookie
diff --git a/websdk/werkzeug/wrappers.py b/websdk/werkzeug/wrappers.py
new file mode 100644
index 0000000..88b5b63
--- /dev/null
+++ b/websdk/werkzeug/wrappers.py
@@ -0,0 +1,1641 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.wrappers
+ ~~~~~~~~~~~~~~~~~
+
+ The wrappers are simple request and response objects which you can
+ subclass to do whatever you want them to do. The request object contains
+ the information transmitted by the client (webbrowser) and the response
+ object contains all the information sent back to the browser.
+
+ An important detail is that the request object is created with the WSGI
+ environ and will act as high-level proxy whereas the response object is an
+ actual WSGI application.
+
+ Like everything else in Werkzeug these objects will work correctly with
+ unicode data. Incoming form data parsed by the response object will be
+ decoded into an unicode object if possible and if it makes sense.
+
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import urlparse
+from datetime import datetime, timedelta
+
+from werkzeug.http import HTTP_STATUS_CODES, \
+ parse_accept_header, parse_cache_control_header, parse_etags, \
+ parse_date, generate_etag, is_resource_modified, unquote_etag, \
+ quote_etag, parse_set_header, parse_authorization_header, \
+ parse_www_authenticate_header, remove_entity_headers, \
+ parse_options_header, dump_options_header, http_date, \
+ parse_if_range_header, parse_cookie, dump_cookie, \
+ parse_range_header, parse_content_range_header, dump_header
+from werkzeug.urls import url_decode, iri_to_uri
+from werkzeug.formparser import FormDataParser, default_stream_factory
+from werkzeug.utils import cached_property, environ_property, \
+ header_property, get_content_type
+from werkzeug.wsgi import get_current_url, get_host, LimitedStream, \
+ ClosingIterator
+from werkzeug.datastructures import MultiDict, CombinedMultiDict, Headers, \
+ EnvironHeaders, ImmutableMultiDict, ImmutableTypeConversionDict, \
+ ImmutableList, MIMEAccept, CharsetAccept, LanguageAccept, \
+ ResponseCacheControl, RequestCacheControl, CallbackDict, \
+ ContentRange
+from werkzeug._internal import _empty_stream, _decode_unicode, \
+ _patch_wrapper, _get_environ
+
+
+def _run_wsgi_app(*args):
+ """This function replaces itself to ensure that the test module is not
+ imported unless required. DO NOT USE!
+ """
+ global _run_wsgi_app
+ from werkzeug.test import run_wsgi_app as _run_wsgi_app
+ return _run_wsgi_app(*args)
+
+
+def _warn_if_string(iterable):
+ """Helper for the response objects to check if the iterable returned
+ to the WSGI server is not a string.
+ """
+ if isinstance(iterable, basestring):
+ from warnings import warn
+ warn(Warning('response iterable was set to a string. This appears '
+ 'to work but means that the server will send the '
+ 'data to the client char, by char. This is almost '
+ 'never intended behavior, use response.data to assign '
+ 'strings to the response object.'), stacklevel=2)
+
+
+class BaseRequest(object):
+ """Very basic request object. This does not implement advanced stuff like
+ entity tag parsing or cache controls. The request object is created with
+ the WSGI environment as first argument and will add itself to the WSGI
+ environment as ``'werkzeug.request'`` unless it's created with
+ `populate_request` set to False.
+
+ There are a couple of mixins available that add additional functionality
+ to the request object, there is also a class called `Request` which
+ subclasses `BaseRequest` and all the important mixins.
+
+ It's a good idea to create a custom subclass of the :class:`BaseRequest`
+ and add missing functionality either via mixins or direct implementation.
+ Here an example for such subclasses::
+
+ from werkzeug.wrappers import BaseRequest, ETagRequestMixin
+
+ class Request(BaseRequest, ETagRequestMixin):
+ pass
+
+ Request objects are **read only**. As of 0.5 modifications are not
+ allowed in any place. Unlike the lower level parsing functions the
+ request object will use immutable objects everywhere possible.
+
+ Per default the request object will assume all the text data is `utf-8`
+ encoded. Please refer to `the unicode chapter <unicode.txt>`_ for more
+ details about customizing the behavior.
+
+ Per default the request object will be added to the WSGI
+ environment as `werkzeug.request` to support the debugging system.
+ If you don't want that, set `populate_request` to `False`.
+
+ If `shallow` is `True` the environment is initialized as shallow
+ object around the environ. Every operation that would modify the
+ environ in any way (such as consuming form data) raises an exception
+ unless the `shallow` attribute is explicitly set to `False`. This
+ is useful for middlewares where you don't want to consume the form
+ data by accident. A shallow request is not populated to the WSGI
+ environment.
+
+ .. versionchanged:: 0.5
+ read-only mode was enforced by using immutables classes for all
+ data.
+ """
+
+ #: the charset for the request, defaults to utf-8
+ charset = 'utf-8'
+
+ #: the error handling procedure for errors, defaults to 'replace'
+ encoding_errors = 'replace'
+
+ #: the maximum content length. This is forwarded to the form data
+ #: parsing function (:func:`parse_form_data`). When set and the
+ #: :attr:`form` or :attr:`files` attribute is accessed and the
+ #: parsing fails because more than the specified value is transmitted
+ #: a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised.
+ #:
+ #: Have a look at :ref:`dealing-with-request-data` for more details.
+ #:
+ #: .. versionadded:: 0.5
+ max_content_length = None
+
+ #: the maximum form field size. This is forwarded to the form data
+ #: parsing function (:func:`parse_form_data`). When set and the
+ #: :attr:`form` or :attr:`files` attribute is accessed and the
+ #: data in memory for post data is longer than the specified value a
+ #: :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised.
+ #:
+ #: Have a look at :ref:`dealing-with-request-data` for more details.
+ #:
+ #: .. versionadded:: 0.5
+ max_form_memory_size = None
+
+ #: the class to use for `args` and `form`. The default is an
+ #: :class:`~werkzeug.datastructures.ImmutableMultiDict` which supports
+ #: multiple values per key. alternatively it makes sense to use an
+ #: :class:`~werkzeug.datastructures.ImmutableOrderedMultiDict` which
+ #: preserves order or a :class:`~werkzeug.datastructures.ImmutableDict`
+ #: which is the fastest but only remembers the last key. It is also
+ #: possible to use mutable structures, but this is not recommended.
+ #:
+ #: .. versionadded:: 0.6
+ parameter_storage_class = ImmutableMultiDict
+
+ #: the type to be used for list values from the incoming WSGI environment.
+ #: By default an :class:`~werkzeug.datastructures.ImmutableList` is used
+ #: (for example for :attr:`access_list`).
+ #:
+ #: .. versionadded:: 0.6
+ list_storage_class = ImmutableList
+
+ #: the type to be used for dict values from the incoming WSGI environment.
+ #: By default an
+ #: :class:`~werkzeug.datastructures.ImmutableTypeConversionDict` is used
+ #: (for example for :attr:`cookies`).
+ #:
+ #: .. versionadded:: 0.6
+ dict_storage_class = ImmutableTypeConversionDict
+
+ #: The form data parser that shoud be used. Can be replaced to customize
+ #: the form date parsing.
+ form_data_parser_class = FormDataParser
+
+ def __init__(self, environ, populate_request=True, shallow=False):
+ self.environ = environ
+ if populate_request and not shallow:
+ self.environ['werkzeug.request'] = self
+ self.shallow = shallow
+
+ def __repr__(self):
+ # make sure the __repr__ even works if the request was created
+ # from an invalid WSGI environment. If we display the request
+ # in a debug session we don't want the repr to blow up.
+ args = []
+ try:
+ args.append("'%s'" % self.url)
+ args.append('[%s]' % self.method)
+ except Exception:
+ args.append('(invalid WSGI environ)')
+
+ return '<%s %s>' % (
+ self.__class__.__name__,
+ ' '.join(args)
+ )
+
+ @property
+ def url_charset(self):
+ """The charset that is assumed for URLs. Defaults to the value
+ of :attr:`charset`.
+
+ .. versionadded:: 0.6
+ """
+ return self.charset
+
+ @classmethod
+ def from_values(cls, *args, **kwargs):
+ """Create a new request object based on the values provided. If
+ environ is given missing values are filled from there. This method is
+ useful for small scripts when you need to simulate a request from an URL.
+ Do not use this method for unittesting, there is a full featured client
+ object (:class:`Client`) that allows to create multipart requests,
+ support for cookies etc.
+
+ This accepts the same options as the
+ :class:`~werkzeug.test.EnvironBuilder`.
+
+ .. versionchanged:: 0.5
+ This method now accepts the same arguments as
+ :class:`~werkzeug.test.EnvironBuilder`. Because of this the
+ `environ` parameter is now called `environ_overrides`.
+
+ :return: request object
+ """
+ from werkzeug.test import EnvironBuilder
+ charset = kwargs.pop('charset', cls.charset)
+ builder = EnvironBuilder(*args, **kwargs)
+ try:
+ return builder.get_request(cls)
+ finally:
+ builder.close()
+
+ @classmethod
+ def application(cls, f):
+ """Decorate a function as responder that accepts the request as first
+ argument. This works like the :func:`responder` decorator but the
+ function is passed the request object as first argument::
+
+ @Request.application
+ def my_wsgi_app(request):
+ return Response('Hello World!')
+
+ :param f: the WSGI callable to decorate
+ :return: a new WSGI callable
+ """
+ #: return a callable that wraps the -2nd argument with the request
+ #: and calls the function with all the arguments up to that one and
+ #: the request. The return value is then called with the latest
+ #: two arguments. This makes it possible to use this decorator for
+ #: both methods and standalone WSGI functions.
+ return _patch_wrapper(f, lambda *a: f(*a[:-2]+(cls(a[-2]),))(*a[-2:]))
+
+ def _get_file_stream(self, total_content_length, content_type, filename=None,
+ content_length=None):
+ """Called to get a stream for the file upload.
+
+ This must provide a file-like class with `read()`, `readline()`
+ and `seek()` methods that is both writeable and readable.
+
+ The default implementation returns a temporary file if the total
+ content length is higher than 500KB. Because many browsers do not
+ provide a content length for the files only the total content
+ length matters.
+
+ :param total_content_length: the total content length of all the
+ data in the request combined. This value
+ is guaranteed to be there.
+ :param content_type: the mimetype of the uploaded file.
+ :param filename: the filename of the uploaded file. May be `None`.
+ :param content_length: the length of this file. This value is usually
+ not provided because webbrowsers do not provide
+ this value.
+ """
+ return default_stream_factory(total_content_length, content_type,
+ filename, content_length)
+
+ @property
+ def want_form_data_parsed(self):
+ """Returns True if the request method is ``POST``, ``PUT`` or
+ ``PATCH``. Can be overriden to support other HTTP methods that
+ should carry form data.
+
+ .. versionadded:: 0.8
+ """
+ return self.environ['REQUEST_METHOD'] in ('POST', 'PUT', 'PATCH')
+
+ def make_form_data_parser(self):
+ """Creates the form data parser. Instanciates the
+ :attr:`form_data_parser_class` with some parameters.
+
+ .. versionadded:: 0.8
+ """
+ return self.form_data_parser_class(self._get_file_stream,
+ self.charset,
+ self.encoding_errors,
+ self.max_form_memory_size,
+ self.max_content_length,
+ self.parameter_storage_class)
+
+ def _load_form_data(self):
+ """Method used internally to retrieve submitted data. After calling
+ this sets `form` and `files` on the request object to multi dicts
+ filled with the incoming form data. As a matter of fact the input
+ stream will be empty afterwards.
+
+ .. versionadded:: 0.8
+ """
+ # abort early if we have already consumed the stream
+ if 'stream' in self.__dict__:
+ return
+ if self.shallow:
+ raise RuntimeError('A shallow request tried to consume '
+ 'form data. If you really want to do '
+ 'that, set `shallow` to False.')
+ data = None
+ stream = _empty_stream
+ if self.want_form_data_parsed:
+ parser = self.make_form_data_parser()
+ data = parser.parse_from_environ(self.environ)
+ else:
+ # if we have a content length header we are able to properly
+ # guard the incoming stream, no matter what request method is
+ # used.
+ content_length = self.headers.get('content-length', type=int)
+ if content_length is not None:
+ stream = LimitedStream(self.environ['wsgi.input'],
+ content_length)
+
+ if data is None:
+ data = (stream, self.parameter_storage_class(),
+ self.parameter_storage_class())
+
+ # inject the values into the instance dict so that we bypass
+ # our cached_property non-data descriptor.
+ d = self.__dict__
+ d['stream'], d['form'], d['files'] = data
+
+ @cached_property
+ def stream(self):
+ """The parsed stream if the submitted data was not multipart or
+ urlencoded form data. This stream is the stream left by the form data
+ parser module after parsing. This is *not* the WSGI input stream but
+ a wrapper around it that ensures the caller does not accidentally
+ read past `Content-Length`.
+ """
+ self._load_form_data()
+ return self.stream
+
+ input_stream = environ_property('wsgi.input', 'The WSGI input stream.\n'
+ 'In general it\'s a bad idea to use this one because you can easily '
+ 'read past the boundary. Use the :attr:`stream` instead.')
+
+ @cached_property
+ def args(self):
+ """The parsed URL parameters. By default an
+ :class:`~werkzeug.datastructures.ImmutableMultiDict`
+ is returned from this function. This can be changed by setting
+ :attr:`parameter_storage_class` to a different type. This might
+ be necessary if the order of the form data is important.
+ """
+ return url_decode(self.environ.get('QUERY_STRING', ''),
+ self.url_charset, errors=self.encoding_errors,
+ cls=self.parameter_storage_class)
+
+ @cached_property
+ def data(self):
+ """This reads the buffered incoming data from the client into the
+ string. Usually it's a bad idea to access :attr:`data` because a client
+ could send dozens of megabytes or more to cause memory problems on the
+ server.
+
+ To circumvent that make sure to check the content length first.
+ """
+ return self.stream.read()
+
+ @cached_property
+ def form(self):
+ """The form parameters. By default an
+ :class:`~werkzeug.datastructures.ImmutableMultiDict`
+ is returned from this function. This can be changed by setting
+ :attr:`parameter_storage_class` to a different type. This might
+ be necessary if the order of the form data is important.
+ """
+ self._load_form_data()
+ return self.form
+
+ @cached_property
+ def values(self):
+ """Combined multi dict for :attr:`args` and :attr:`form`."""
+ args = []
+ for d in self.args, self.form:
+ if not isinstance(d, MultiDict):
+ d = MultiDict(d)
+ args.append(d)
+ return CombinedMultiDict(args)
+
+ @cached_property
+ def files(self):
+ """:class:`~werkzeug.datastructures.MultiDict` object containing
+ all uploaded files. Each key in :attr:`files` is the name from the
+ ``<input type="file" name="">``. Each value in :attr:`files` is a
+ Werkzeug :class:`~werkzeug.datastructures.FileStorage` object.
+
+ Note that :attr:`files` will only contain data if the request method was
+ POST, PUT or PATCH and the ``<form>`` that posted to the request had
+ ``enctype="multipart/form-data"``. It will be empty otherwise.
+
+ See the :class:`~werkzeug.datastructures.MultiDict` /
+ :class:`~werkzeug.datastructures.FileStorage` documentation for
+ more details about the used data structure.
+ """
+ self._load_form_data()
+ return self.files
+
+ @cached_property
+ def cookies(self):
+ """Read only access to the retrieved cookie values as dictionary."""
+ return parse_cookie(self.environ, self.charset,
+ cls=self.dict_storage_class)
+
+ @cached_property
+ def headers(self):
+ """The headers from the WSGI environ as immutable
+ :class:`~werkzeug.datastructures.EnvironHeaders`.
+ """
+ return EnvironHeaders(self.environ)
+
+ @cached_property
+ def path(self):
+ """Requested path as unicode. This works a bit like the regular path
+ info in the WSGI environment but will always include a leading slash,
+ even if the URL root is accessed.
+ """
+ path = '/' + (self.environ.get('PATH_INFO') or '').lstrip('/')
+ return _decode_unicode(path, self.url_charset, self.encoding_errors)
+
+ @cached_property
+ def script_root(self):
+ """The root path of the script without the trailing slash."""
+ path = (self.environ.get('SCRIPT_NAME') or '').rstrip('/')
+ return _decode_unicode(path, self.url_charset, self.encoding_errors)
+
+ @cached_property
+ def url(self):
+ """The reconstructed current URL"""
+ return get_current_url(self.environ)
+
+ @cached_property
+ def base_url(self):
+ """Like :attr:`url` but without the querystring"""
+ return get_current_url(self.environ, strip_querystring=True)
+
+ @cached_property
+ def url_root(self):
+ """The full URL root (with hostname), this is the application root."""
+ return get_current_url(self.environ, True)
+
+ @cached_property
+ def host_url(self):
+ """Just the host with scheme."""
+ return get_current_url(self.environ, host_only=True)
+
+ @cached_property
+ def host(self):
+ """Just the host including the port if available."""
+ return get_host(self.environ)
+
+ query_string = environ_property('QUERY_STRING', '', read_only=True, doc=
+ '''The URL parameters as raw bytestring.''')
+ method = environ_property('REQUEST_METHOD', 'GET', read_only=True, doc=
+ '''The transmission method. (For example ``'GET'`` or ``'POST'``).''')
+
+ @cached_property
+ def access_route(self):
+ """If a forwarded header exists this is a list of all ip addresses
+ from the client ip to the last proxy server.
+ """
+ if 'HTTP_X_FORWARDED_FOR' in self.environ:
+ addr = self.environ['HTTP_X_FORWARDED_FOR'].split(',')
+ return self.list_storage_class([x.strip() for x in addr])
+ elif 'REMOTE_ADDR' in self.environ:
+ return self.list_storage_class([self.environ['REMOTE_ADDR']])
+ return self.list_storage_class()
+
+ @property
+ def remote_addr(self):
+ """The remote address of the client."""
+ return self.environ.get('REMOTE_ADDR')
+
+ remote_user = environ_property('REMOTE_USER', doc='''
+ If the server supports user authentication, and the script is
+ protected, this attribute contains the username the user has
+ authenticated as.''')
+
+ scheme = environ_property('wsgi.url_scheme', doc='''
+ URL scheme (http or https).
+
+ .. versionadded:: 0.7''')
+
+ is_xhr = property(lambda x: x.environ.get('HTTP_X_REQUESTED_WITH', '')
+ .lower() == 'xmlhttprequest', doc='''
+ 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.''')
+ is_secure = property(lambda x: x.environ['wsgi.url_scheme'] == 'https',
+ doc='`True` if the request is secure.')
+ is_multithread = environ_property('wsgi.multithread', doc='''
+ boolean that is `True` if the application is served by
+ a multithreaded WSGI server.''')
+ is_multiprocess = environ_property('wsgi.multiprocess', doc='''
+ boolean that is `True` if the application is served by
+ a WSGI server that spawns multiple processes.''')
+ is_run_once = environ_property('wsgi.run_once', doc='''
+ boolean that is `True` if the application will be executed only
+ once in a process lifetime. This is the case for CGI for example,
+ but it's not guaranteed that the exeuction only happens one time.''')
+
+
+class BaseResponse(object):
+ """Base response class. The most important fact about a response object
+ is that it's a regular WSGI application. It's initialized with a couple
+ of response parameters (headers, body, status code etc.) and will start a
+ valid WSGI response when called with the environ and start response
+ callable.
+
+ Because it's a WSGI application itself processing usually ends before the
+ actual response is sent to the server. This helps debugging systems
+ because they can catch all the exceptions before responses are started.
+
+ Here a small example WSGI application that takes advantage of the
+ response objects::
+
+ from werkzeug.wrappers import BaseResponse as Response
+
+ def index():
+ return Response('Index page')
+
+ def application(environ, start_response):
+ path = environ.get('PATH_INFO') or '/'
+ if path == '/':
+ response = index()
+ else:
+ response = Response('Not Found', status=404)
+ return response(environ, start_response)
+
+ Like :class:`BaseRequest` which object is lacking a lot of functionality
+ implemented in mixins. This gives you a better control about the actual
+ API of your response objects, so you can create subclasses and add custom
+ functionality. A full featured response object is available as
+ :class:`Response` which implements a couple of useful mixins.
+
+ To enforce a new type of already existing responses you can use the
+ :meth:`force_type` method. This is useful if you're working with different
+ subclasses of response objects and you want to post process them with a
+ know interface.
+
+ Per default the request object will assume all the text data is `utf-8`
+ encoded. Please refer to `the unicode chapter <unicode.txt>`_ for more
+ details about customizing the behavior.
+
+ Response can be any kind of iterable or string. If it's a string it's
+ considered being an iterable with one item which is the string passed.
+ Headers can be a list of tuples or a
+ :class:`~werkzeug.datastructures.Headers` object.
+
+ Special note for `mimetype` and `content_type`: For most mime types
+ `mimetype` and `content_type` work the same, the difference affects
+ only 'text' mimetypes. If the mimetype passed with `mimetype` is a
+ mimetype starting with `text/` it becomes a charset parameter defined
+ with the charset of the response object. In contrast the
+ `content_type` parameter is always added as header unmodified.
+
+ .. versionchanged:: 0.5
+ the `direct_passthrough` parameter was added.
+
+ :param response: a string or response iterable.
+ :param status: a string with a status or an integer with the status code.
+ :param headers: a list of headers or a
+ :class:`~werkzeug.datastructures.Headers` object.
+ :param mimetype: the mimetype for the request. See notice above.
+ :param content_type: the content type for the request. See notice above.
+ :param direct_passthrough: if set to `True` :meth:`iter_encoded` is not
+ called before iteration which makes it
+ possible to pass special iterators though
+ unchanged (see :func:`wrap_file` for more
+ details.)
+ """
+
+ #: the charset of the response.
+ charset = 'utf-8'
+
+ #: the default status if none is provided.
+ default_status = 200
+
+ #: the default mimetype if none is provided.
+ default_mimetype = 'text/plain'
+
+ #: if set to `False` accessing properties on the response object will
+ #: not try to consume the response iterator and convert it into a list.
+ #:
+ #: .. versionadded:: 0.6.2
+ #:
+ #: That attribute was previously called `implicit_seqence_conversion`.
+ #: (Notice the typo). If you did use this feature, you have to adapt
+ #: your code to the name change.
+ implicit_sequence_conversion = True
+
+ #: Should this response object correct the location header to be RFC
+ #: conformant? This is true by default.
+ #:
+ #: .. versionadded:: 0.8
+ autocorrect_location_header = True
+
+ #: Should this response object automatically set the content-length
+ #: header if possible? This is true by default.
+ #:
+ #: .. versionadded:: 0.8
+ automatically_set_content_length = True
+
+ def __init__(self, response=None, status=None, headers=None,
+ mimetype=None, content_type=None, direct_passthrough=False):
+ if isinstance(headers, Headers):
+ self.headers = headers
+ elif not headers:
+ self.headers = Headers()
+ else:
+ self.headers = Headers(headers)
+
+ if content_type is None:
+ if mimetype is None and 'content-type' not in self.headers:
+ mimetype = self.default_mimetype
+ if mimetype is not None:
+ mimetype = get_content_type(mimetype, self.charset)
+ content_type = mimetype
+ if content_type is not None:
+ self.headers['Content-Type'] = content_type
+ if status is None:
+ status = self.default_status
+ if isinstance(status, (int, long)):
+ self.status_code = status
+ else:
+ self.status = status
+
+ self.direct_passthrough = direct_passthrough
+ self._on_close = []
+
+ # we set the response after the headers so that if a class changes
+ # the charset attribute, the data is set in the correct charset.
+ if response is None:
+ self.response = []
+ elif isinstance(response, basestring):
+ self.data = response
+ else:
+ self.response = response
+
+ def call_on_close(self, func):
+ """Adds a function to the internal list of functions that should
+ be called as part of closing down the response. Since 0.7 this
+ function also returns the function that was passed so that this
+ can be used as a decorator.
+
+ .. versionadded:: 0.6
+ """
+ self._on_close.append(func)
+ return func
+
+ def __repr__(self):
+ if self.is_sequence:
+ body_info = '%d bytes' % sum(map(len, self.iter_encoded()))
+ else:
+ body_info = self.is_streamed and 'streamed' or 'likely-streamed'
+ return '<%s %s [%s]>' % (
+ self.__class__.__name__,
+ body_info,
+ self.status
+ )
+
+ @classmethod
+ def force_type(cls, response, environ=None):
+ """Enforce that the WSGI response is a response object of the current
+ type. Werkzeug will use the :class:`BaseResponse` internally in many
+ situations like the exceptions. If you call :meth:`get_response` on an
+ exception you will get back a regular :class:`BaseResponse` object, even
+ if you are using a custom subclass.
+
+ This method can enforce a given response type, and it will also
+ convert arbitrary WSGI callables into response objects if an environ
+ is provided::
+
+ # convert a Werkzeug response object into an instance of the
+ # MyResponseClass subclass.
+ response = MyResponseClass.force_type(response)
+
+ # convert any WSGI application into a response object
+ response = MyResponseClass.force_type(response, environ)
+
+ This is especially useful if you want to post-process responses in
+ the main dispatcher and use functionality provided by your subclass.
+
+ Keep in mind that this will modify response objects in place if
+ possible!
+
+ :param response: a response object or wsgi application.
+ :param environ: a WSGI environment object.
+ :return: a response object.
+ """
+ if not isinstance(response, BaseResponse):
+ if environ is None:
+ raise TypeError('cannot convert WSGI application into '
+ 'response objects without an environ')
+ response = BaseResponse(*_run_wsgi_app(response, environ))
+ response.__class__ = cls
+ return response
+
+ @classmethod
+ def from_app(cls, app, environ, buffered=False):
+ """Create a new response object from an application output. This
+ works best if you pass it an application that returns a generator all
+ the time. Sometimes applications may use the `write()` callable
+ returned by the `start_response` function. This tries to resolve such
+ edge cases automatically. But if you don't get the expected output
+ you should set `buffered` to `True` which enforces buffering.
+
+ :param app: the WSGI application to execute.
+ :param environ: the WSGI environment to execute against.
+ :param buffered: set to `True` to enforce buffering.
+ :return: a response object.
+ """
+ return cls(*_run_wsgi_app(app, environ, buffered))
+
+ def _get_status_code(self):
+ return self._status_code
+ def _set_status_code(self, code):
+ self._status_code = code
+ try:
+ self._status = '%d %s' % (code, HTTP_STATUS_CODES[code].upper())
+ except KeyError:
+ self._status = '%d UNKNOWN' % code
+ status_code = property(_get_status_code, _set_status_code,
+ 'The HTTP Status code as number')
+ del _get_status_code, _set_status_code
+
+ def _get_status(self):
+ return self._status
+ def _set_status(self, value):
+ self._status = value
+ try:
+ self._status_code = int(self._status.split(None, 1)[0])
+ except ValueError:
+ self._status_code = 0
+ status = property(_get_status, _set_status, 'The HTTP Status code')
+ del _get_status, _set_status
+
+ def _get_data(self):
+ """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.
+
+ This behavior can be disabled by setting
+ :attr:`implicit_sequence_conversion` to `False`.
+ """
+ self._ensure_sequence()
+ return ''.join(self.iter_encoded())
+ def _set_data(self, value):
+ # if an unicode string is set, it's encoded directly so that we
+ # can set the content length
+ if isinstance(value, unicode):
+ value = value.encode(self.charset)
+ self.response = [value]
+ if self.automatically_set_content_length:
+ self.headers['Content-Length'] = str(len(value))
+ data = property(_get_data, _set_data, doc=_get_data.__doc__)
+ del _get_data, _set_data
+
+ def _ensure_sequence(self, mutable=False):
+ """This method can be called by methods that need a sequence. If
+ `mutable` is true, it will also ensure that the response sequence
+ is a standard Python list.
+
+ .. versionadded:: 0.6
+ """
+ if self.is_sequence:
+ # if we need a mutable object, we ensure it's a list.
+ if mutable and not isinstance(self.response, list):
+ self.response = list(self.response)
+ return
+ if not self.implicit_sequence_conversion:
+ raise RuntimeError('The response object required the iterable '
+ 'to be a sequence, but the implicit '
+ 'conversion was disabled. Call '
+ 'make_sequence() yourself.')
+ self.make_sequence()
+
+ def make_sequence(self):
+ """Converts the response iterator in a list. By default this happens
+ automatically if required. If `implicit_sequence_conversion` is
+ disabled, this method is not automatically called and some properties
+ might raise exceptions. This also encodes all the items.
+
+ .. versionadded:: 0.6
+ """
+ if not self.is_sequence:
+ # if we consume an iterable we have to ensure that the close
+ # method of the iterable is called if available when we tear
+ # down the response
+ close = getattr(self.response, 'close', None)
+ self.response = list(self.iter_encoded())
+ if close is not None:
+ self.call_on_close(close)
+
+ def iter_encoded(self, charset=None):
+ """Iter the response encoded with the encoding of the response.
+ If the response object is invoked as WSGI application the return
+ value of this method is used as application iterator unless
+ :attr:`direct_passthrough` was activated.
+
+ .. versionchanged:: 0.6
+
+ The `charset` parameter was deprecated and became a no-op.
+ """
+ # XXX: deprecated
+ if __debug__ and charset is not None: # pragma: no cover
+ from warnings import warn
+ warn(DeprecationWarning('charset was deprecated and is ignored.'),
+ stacklevel=2)
+ charset = self.charset
+ if __debug__:
+ _warn_if_string(self.response)
+ for item in self.response:
+ if isinstance(item, unicode):
+ yield item.encode(charset)
+ else:
+ yield str(item)
+
+ def set_cookie(self, key, value='', max_age=None, expires=None,
+ path='/', domain=None, secure=None, httponly=False):
+ """Sets a cookie. The parameters are the same as in the cookie `Morsel`
+ object in the Python standard library but it accepts unicode data, too.
+
+ :param key: the key (name) of the cookie to be set.
+ :param value: the value of the cookie.
+ :param max_age: should be a number of seconds, or `None` (default) if
+ the cookie should last only as long as the client's
+ browser session.
+ :param expires: should be a `datetime` object or UNIX timestamp.
+ :param domain: if you want to set a cross-domain cookie. For example,
+ ``domain=".example.com"`` will set a cookie that is
+ readable by the domain ``www.example.com``,
+ ``foo.example.com`` etc. Otherwise, a cookie will only
+ be readable by the domain that set it.
+ :param path: limits the cookie to a given path, per default it will
+ span the whole domain.
+ """
+ self.headers.add('Set-Cookie', dump_cookie(key, value, max_age,
+ expires, path, domain, secure, httponly,
+ self.charset))
+
+ def delete_cookie(self, key, path='/', domain=None):
+ """Delete a cookie. Fails silently if key doesn't exist.
+
+ :param key: the key (name) of the cookie to be deleted.
+ :param path: if the cookie that should be deleted was limited to a
+ path, the path has to be defined here.
+ :param domain: if the cookie that should be deleted was limited to a
+ domain, that domain has to be defined here.
+ """
+ self.set_cookie(key, expires=0, max_age=0, path=path, domain=domain)
+
+ @property
+ def header_list(self): # pragma: no cover
+ # XXX: deprecated
+ if __debug__:
+ from warnings import warn
+ warn(DeprecationWarning('header_list is deprecated'),
+ stacklevel=2)
+ return self.headers.to_list(self.charset)
+
+ @property
+ def is_streamed(self):
+ """If the response is streamed (the response is not an iterable with
+ a length information) this property is `True`. In this case streamed
+ means that there is no information about the number of iterations.
+ This is usually `True` if a generator is passed to the response object.
+
+ This is useful for checking before applying some sort of post
+ filtering that should not take place for streamed responses.
+ """
+ try:
+ len(self.response)
+ except TypeError:
+ return True
+ return False
+
+ @property
+ def is_sequence(self):
+ """If the iterator is buffered, this property will be `True`. A
+ response object will consider an iterator to be buffered if the
+ response attribute is a list or tuple.
+
+ .. versionadded:: 0.6
+ """
+ return isinstance(self.response, (tuple, list))
+
+ def close(self):
+ """Close the wrapped response if possible."""
+ if hasattr(self.response, 'close'):
+ self.response.close()
+ for func in self._on_close:
+ func()
+
+ def freeze(self):
+ """Call this method if you want to make your response object ready for
+ being pickled. This buffers the generator if there is one. It will
+ also set the `Content-Length` header to the length of the body.
+
+ .. versionchanged:: 0.6
+ The `Content-Length` header is now set.
+ """
+ # we explicitly set the length to a list of the *encoded* response
+ # iterator. Even if the implicit sequence conversion is disabled.
+ self.response = list(self.iter_encoded())
+ self.headers['Content-Length'] = str(sum(map(len, self.response)))
+
+ def fix_headers(self, environ):
+ # XXX: deprecated
+ if __debug__:
+ from warnings import warn
+ warn(DeprecationWarning('called into deprecated fix_headers baseclass '
+ 'method. Use get_wsgi_headers instead.'),
+ stacklevel=2)
+ self.headers[:] = self.get_wsgi_headers(environ)
+
+ def get_wsgi_headers(self, environ):
+ """This is automatically called right before the response is started
+ and returns headers modified for the given environment. It returns a
+ copy of the headers from the response with some modifications applied
+ if necessary.
+
+ For example the location header (if present) is joined with the root
+ URL of the environment. Also the content length is automatically set
+ to zero here for certain status codes.
+
+ .. versionchanged:: 0.6
+ Previously that function was called `fix_headers` and modified
+ the response object in place. Also since 0.6, IRIs in location
+ and content-location headers are handled properly.
+
+ Also starting with 0.6, Werkzeug will attempt to set the content
+ length if it is able to figure it out on its own. This is the
+ case if all the strings in the response iterable are already
+ encoded and the iterable is buffered.
+
+ :param environ: the WSGI environment of the request.
+ :return: returns a new :class:`~werkzeug.datastructures.Headers`
+ object.
+ """
+ headers = Headers(self.headers)
+ location = None
+ content_location = None
+ content_length = None
+ status = self.status_code
+
+ # iterate over the headers to find all values in one go. Because
+ # get_wsgi_headers is used each response that gives us a tiny
+ # speedup.
+ for key, value in headers:
+ ikey = key.lower()
+ if ikey == 'location':
+ location = value
+ elif ikey == 'content-location':
+ content_location = value
+ elif ikey == 'content-length':
+ content_length = value
+
+ # make sure the location header is an absolute URL
+ if location is not None:
+ old_location = location
+ if isinstance(location, unicode):
+ location = iri_to_uri(location)
+ if self.autocorrect_location_header:
+ location = urlparse.urljoin(
+ get_current_url(environ, root_only=True),
+ location
+ )
+ if location != old_location:
+ headers['Location'] = location
+
+ # make sure the content location is a URL
+ if content_location is not None and \
+ isinstance(content_location, unicode):
+ headers['Content-Location'] = iri_to_uri(content_location)
+
+ # remove entity headers and set content length to zero if needed.
+ # Also update content_length accordingly so that the automatic
+ # content length detection does not trigger in the following
+ # code.
+ if 100 <= status < 200 or status == 204:
+ headers['Content-Length'] = content_length = '0'
+ elif status == 304:
+ remove_entity_headers(headers)
+
+ # if we can determine the content length automatically, we
+ # should try to do that. But only if this does not involve
+ # flattening the iterator or encoding of unicode strings in
+ # the response. We however should not do that if we have a 304
+ # response.
+ if self.automatically_set_content_length and \
+ self.is_sequence and content_length is None and status != 304:
+ try:
+ content_length = sum(len(str(x)) for x in self.response)
+ except UnicodeError:
+ # aha, something non-bytestringy in there, too bad, we
+ # can't safely figure out the length of the response.
+ pass
+ else:
+ headers['Content-Length'] = str(content_length)
+
+ return headers
+
+ def get_app_iter(self, environ):
+ """Returns the application iterator for the given environ. Depending
+ on the request method and the current status code the return value
+ might be an empty response rather than the one from the response.
+
+ If the request method is `HEAD` or the status code is in a range
+ where the HTTP specification requires an empty response, an empty
+ iterable is returned.
+
+ .. versionadded:: 0.6
+
+ :param environ: the WSGI environment of the request.
+ :return: a response iterable.
+ """
+ status = self.status_code
+ if environ['REQUEST_METHOD'] == 'HEAD' or \
+ 100 <= status < 200 or status in (204, 304):
+ return ()
+ if self.direct_passthrough:
+ if __debug__:
+ _warn_if_string(self.response)
+ return self.response
+ return ClosingIterator(self.iter_encoded(), self.close)
+
+ def get_wsgi_response(self, environ):
+ """Returns the final WSGI response as tuple. The first item in
+ the tuple is the application iterator, the second the status and
+ the third the list of headers. The response returned is created
+ specially for the given environment. For example if the request
+ method in the WSGI environment is ``'HEAD'`` the response will
+ be empty and only the headers and status code will be present.
+
+ .. versionadded:: 0.6
+
+ :param environ: the WSGI environment of the request.
+ :return: an ``(app_iter, status, headers)`` tuple.
+ """
+ # XXX: code for backwards compatibility with custom fix_headers
+ # methods.
+ if self.fix_headers.func_code is not \
+ BaseResponse.fix_headers.func_code:
+ if __debug__:
+ from warnings import warn
+ warn(DeprecationWarning('fix_headers changed behavior in 0.6 '
+ 'and is now called get_wsgi_headers. '
+ 'See documentation for more details.'),
+ stacklevel=2)
+ self.fix_headers(environ)
+ headers = self.headers
+ else:
+ headers = self.get_wsgi_headers(environ)
+ app_iter = self.get_app_iter(environ)
+ return app_iter, self.status, headers.to_list()
+
+ def __call__(self, environ, start_response):
+ """Process this response as WSGI application.
+
+ :param environ: the WSGI environment.
+ :param start_response: the response callable provided by the WSGI
+ server.
+ :return: an application iterator
+ """
+ app_iter, status, headers = self.get_wsgi_response(environ)
+ start_response(status, headers)
+ return app_iter
+
+
+class AcceptMixin(object):
+ """A mixin for classes with an :attr:`~BaseResponse.environ` attribute
+ to get all the HTTP accept headers as
+ :class:`~werkzeug.datastructures.Accept` objects (or subclasses
+ thereof).
+ """
+
+ @cached_property
+ def accept_mimetypes(self):
+ """List of mimetypes this client supports as
+ :class:`~werkzeug.datastructures.MIMEAccept` object.
+ """
+ return parse_accept_header(self.environ.get('HTTP_ACCEPT'), MIMEAccept)
+
+ @cached_property
+ def accept_charsets(self):
+ """List of charsets this client supports as
+ :class:`~werkzeug.datastructures.CharsetAccept` object.
+ """
+ return parse_accept_header(self.environ.get('HTTP_ACCEPT_CHARSET'),
+ CharsetAccept)
+
+ @cached_property
+ def accept_encodings(self):
+ """List of encodings this client accepts. Encodings in a HTTP term
+ are compression encodings such as gzip. For charsets have a look at
+ :attr:`accept_charset`.
+ """
+ return parse_accept_header(self.environ.get('HTTP_ACCEPT_ENCODING'))
+
+ @cached_property
+ def accept_languages(self):
+ """List of languages this client accepts as
+ :class:`~werkzeug.datastructures.LanguageAccept` object.
+
+ .. versionchanged 0.5
+ In previous versions this was a regular
+ :class:`~werkzeug.datastructures.Accept` object.
+ """
+ return parse_accept_header(self.environ.get('HTTP_ACCEPT_LANGUAGE'),
+ LanguageAccept)
+
+
+class ETagRequestMixin(object):
+ """Add entity tag and cache descriptors to a request object or object with
+ a WSGI environment available as :attr:`~BaseRequest.environ`. This not
+ only provides access to etags but also to the cache control header.
+ """
+
+ @cached_property
+ def cache_control(self):
+ """A :class:`~werkzeug.datastructures.RequestCacheControl` object
+ for the incoming cache control headers.
+ """
+ cache_control = self.environ.get('HTTP_CACHE_CONTROL')
+ return parse_cache_control_header(cache_control, None,
+ RequestCacheControl)
+
+ @cached_property
+ def if_match(self):
+ """An object containing all the etags in the `If-Match` header.
+
+ :rtype: :class:`~werkzeug.datastructures.ETags`
+ """
+ return parse_etags(self.environ.get('HTTP_IF_MATCH'))
+
+ @cached_property
+ def if_none_match(self):
+ """An object containing all the etags in the `If-None-Match` header.
+
+ :rtype: :class:`~werkzeug.datastructures.ETags`
+ """
+ return parse_etags(self.environ.get('HTTP_IF_NONE_MATCH'))
+
+ @cached_property
+ def if_modified_since(self):
+ """The parsed `If-Modified-Since` header as datetime object."""
+ return parse_date(self.environ.get('HTTP_IF_MODIFIED_SINCE'))
+
+ @cached_property
+ def if_unmodified_since(self):
+ """The parsed `If-Unmodified-Since` header as datetime object."""
+ return parse_date(self.environ.get('HTTP_IF_UNMODIFIED_SINCE'))
+
+ @cached_property
+ def if_range(self):
+ """The parsed `If-Range` header.
+
+ .. versionadded:: 0.7
+
+ :rtype: :class:`~werkzeug.datastructures.IfRange`
+ """
+ return parse_if_range_header(self.environ.get('HTTP_IF_RANGE'))
+
+ @cached_property
+ def range(self):
+ """The parsed `Range` header.
+
+ .. versionadded:: 0.7
+
+ :rtype: :class:`~werkzeug.datastructures.Range`
+ """
+ return parse_range_header(self.environ.get('HTTP_RANGE'))
+
+
+class UserAgentMixin(object):
+ """Adds a `user_agent` attribute to the request object which contains the
+ parsed user agent of the browser that triggered the request as a
+ :class:`~werkzeug.useragents.UserAgent` object.
+ """
+
+ @cached_property
+ def user_agent(self):
+ """The current user agent."""
+ from werkzeug.useragents import UserAgent
+ return UserAgent(self.environ)
+
+
+class AuthorizationMixin(object):
+ """Adds an :attr:`authorization` property that represents the parsed
+ value of the `Authorization` header as
+ :class:`~werkzeug.datastructures.Authorization` object.
+ """
+
+ @cached_property
+ def authorization(self):
+ """The `Authorization` object in parsed form."""
+ header = self.environ.get('HTTP_AUTHORIZATION')
+ return parse_authorization_header(header)
+
+
+class ETagResponseMixin(object):
+ """Adds extra functionality to a response object for etag and cache
+ handling. This mixin requires an object with at least a `headers`
+ object that implements a dict like interface similar to
+ :class:`~werkzeug.datastructures.Headers`.
+
+ If you want the :meth:`freeze` method to automatically add an etag, you
+ have to mixin this method before the response base class. The default
+ response class does not do that.
+ """
+
+ @property
+ def cache_control(self):
+ """The Cache-Control general-header field is used to specify
+ directives that MUST be obeyed by all caching mechanisms along the
+ request/response chain.
+ """
+ def on_update(cache_control):
+ if not cache_control and 'cache-control' in self.headers:
+ del self.headers['cache-control']
+ elif cache_control:
+ self.headers['Cache-Control'] = cache_control.to_header()
+ return parse_cache_control_header(self.headers.get('cache-control'),
+ on_update,
+ ResponseCacheControl)
+
+ def make_conditional(self, request_or_environ):
+ """Make the response conditional to the request. This method works
+ best if an etag was defined for the response already. The `add_etag`
+ method can be used to do that. If called without etag just the date
+ header is set.
+
+ This does nothing if the request method in the request or environ is
+ anything but GET or HEAD.
+
+ It does not remove the body of the response because that's something
+ the :meth:`__call__` function does for us automatically.
+
+ Returns self so that you can do ``return resp.make_conditional(req)``
+ but modifies the object in-place.
+
+ :param request_or_environ: a request object or WSGI environment to be
+ used to make the response conditional
+ against.
+ """
+ environ = _get_environ(request_or_environ)
+ if environ['REQUEST_METHOD'] in ('GET', 'HEAD'):
+ # if the date is not in the headers, add it now. We however
+ # will not override an already existing header. Unfortunately
+ # this header will be overriden by many WSGI servers including
+ # wsgiref.
+ if 'date' not in self.headers:
+ self.headers['Date'] = http_date()
+ if 'content-length' not in self.headers:
+ self.headers['Content-Length'] = len(self.data)
+ if not is_resource_modified(environ, self.headers.get('etag'), None,
+ self.headers.get('last-modified')):
+ self.status_code = 304
+ return self
+
+ def add_etag(self, overwrite=False, weak=False):
+ """Add an etag for the current response if there is none yet."""
+ if overwrite or 'etag' not in self.headers:
+ self.set_etag(generate_etag(self.data), weak)
+
+ def set_etag(self, etag, weak=False):
+ """Set the etag, and override the old one if there was one."""
+ self.headers['ETag'] = quote_etag(etag, weak)
+
+ def get_etag(self):
+ """Return a tuple in the form ``(etag, is_weak)``. If there is no
+ ETag the return value is ``(None, None)``.
+ """
+ return unquote_etag(self.headers.get('ETag'))
+
+ def freeze(self, no_etag=False):
+ """Call this method if you want to make your response object ready for
+ pickeling. This buffers the generator if there is one. This also
+ sets the etag unless `no_etag` is set to `True`.
+ """
+ if not no_etag:
+ self.add_etag()
+ super(ETagResponseMixin, self).freeze()
+
+ accept_ranges = header_property('Accept-Ranges', doc='''
+ The `Accept-Ranges` header. Even though the name would indicate
+ that multiple values are supported, it must be one string token only.
+
+ The values ``'bytes'`` and ``'none'`` are common.
+
+ .. versionadded:: 0.7''')
+
+ def _get_content_range(self):
+ def on_update(rng):
+ if not rng:
+ del self.headers['content-range']
+ else:
+ self.headers['Content-Range'] = rng.to_header()
+ rv = parse_content_range_header(self.headers.get('content-range'),
+ on_update)
+ # always provide a content range object to make the descriptor
+ # more user friendly. It provides an unset() method that can be
+ # used to remove the header quickly.
+ if rv is None:
+ rv = ContentRange(None, None, None, on_update=on_update)
+ return rv
+ def _set_content_range(self, value):
+ if not value:
+ del self.headers['content-range']
+ elif isinstance(value, basestring):
+ self.headers['Content-Range'] = value
+ else:
+ self.headers['Content-Range'] = value.to_header()
+ content_range = property(_get_content_range, _set_content_range, doc='''
+ The `Content-Range` header as
+ :class:`~werkzeug.datastructures.ContentRange` object. Even if the
+ header is not set it wil provide such an object for easier
+ manipulation.
+
+ .. versionadded:: 0.7''')
+ del _get_content_range, _set_content_range
+
+
+class ResponseStream(object):
+ """A file descriptor like object used by the :class:`ResponseStreamMixin` to
+ represent the body of the stream. It directly pushes into the response
+ iterable of the response object.
+ """
+
+ mode = 'wb+'
+
+ def __init__(self, response):
+ self.response = response
+ self.closed = False
+
+ def write(self, value):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ self.response._ensure_sequence(mutable=True)
+ self.response.response.append(value)
+
+ def writelines(self, seq):
+ for item in seq:
+ self.write(item)
+
+ def close(self):
+ self.closed = True
+
+ def flush(self):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+
+ def isatty(self):
+ if self.closed:
+ raise ValueError('I/O operation on closed file')
+ return False
+
+ @property
+ def encoding(self):
+ return self.response.charset
+
+
+class ResponseStreamMixin(object):
+ """Mixin for :class:`BaseRequest` subclasses. Classes that inherit from
+ this mixin will automatically get a :attr:`stream` property that provides
+ a write-only interface to the response iterable.
+ """
+
+ @cached_property
+ def stream(self):
+ """The response iterable as write-only stream."""
+ return ResponseStream(self)
+
+
+class CommonRequestDescriptorsMixin(object):
+ """A mixin for :class:`BaseRequest` subclasses. Request objects that
+ mix this class in will automatically get descriptors for a couple of
+ HTTP headers with automatic type conversion.
+
+ .. versionadded:: 0.5
+ """
+
+ content_type = environ_property('CONTENT_TYPE', doc='''
+ The Content-Type entity-header field indicates the media type of
+ the entity-body sent to the recipient or, in the case of the HEAD
+ method, the media type that would have been sent had the request
+ been a GET.''')
+ content_length = environ_property('CONTENT_LENGTH', None, int, str, doc='''
+ The Content-Length entity-header field indicates the size of the
+ entity-body in bytes or, in the case of the HEAD method, the size of
+ the entity-body that would have been sent had the request been a
+ GET.''')
+ referrer = environ_property('HTTP_REFERER', doc='''
+ The Referer[sic] request-header field allows the client to specify,
+ for the server's benefit, the address (URI) of the resource from which
+ the Request-URI was obtained (the "referrer", although the header
+ field is misspelled).''')
+ date = environ_property('HTTP_DATE', None, parse_date, doc='''
+ The Date general-header field represents the date and time at which
+ the message was originated, having the same semantics as orig-date
+ in RFC 822.''')
+ max_forwards = environ_property('HTTP_MAX_FORWARDS', None, int, doc='''
+ The Max-Forwards request-header field provides a mechanism with the
+ TRACE and OPTIONS methods to limit the number of proxies or gateways
+ that can forward the request to the next inbound server.''')
+
+ def _parse_content_type(self):
+ if not hasattr(self, '_parsed_content_type'):
+ self._parsed_content_type = \
+ parse_options_header(self.environ.get('CONTENT_TYPE', ''))
+
+ @property
+ def mimetype(self):
+ """Like :attr:`content_type` but without parameters (eg, without
+ charset, type etc.). For example if the content
+ type is ``text/html; charset=utf-8`` the mimetype would be
+ ``'text/html'``.
+ """
+ self._parse_content_type()
+ return self._parsed_content_type[0]
+
+ @property
+ def mimetype_params(self):
+ """The mimetype parameters as dict. For example if the content
+ type is ``text/html; charset=utf-8`` the params would be
+ ``{'charset': 'utf-8'}``.
+ """
+ self._parse_content_type()
+ return self._parsed_content_type[1]
+
+ @cached_property
+ def pragma(self):
+ """The Pragma general-header field is used to include
+ implementation-specific directives that might apply to any recipient
+ along the request/response chain. All pragma directives specify
+ optional behavior from the viewpoint of the protocol; however, some
+ systems MAY require that behavior be consistent with the directives.
+ """
+ return parse_set_header(self.environ.get('HTTP_PRAGMA', ''))
+
+
+class CommonResponseDescriptorsMixin(object):
+ """A mixin for :class:`BaseResponse` subclasses. Response objects that
+ mix this class in will automatically get descriptors for a couple of
+ HTTP headers with automatic type conversion.
+ """
+
+ def _get_mimetype(self):
+ ct = self.headers.get('content-type')
+ if ct:
+ return ct.split(';')[0].strip()
+
+ def _set_mimetype(self, value):
+ self.headers['Content-Type'] = get_content_type(value, self.charset)
+
+ def _get_mimetype_params(self):
+ def on_update(d):
+ self.headers['Content-Type'] = \
+ dump_options_header(self.mimetype, d)
+ d = parse_options_header(self.headers.get('content-type', ''))[1]
+ return CallbackDict(d, on_update)
+
+ mimetype = property(_get_mimetype, _set_mimetype, doc='''
+ The mimetype (content type without charset etc.)''')
+ mimetype_params = property(_get_mimetype_params, doc='''
+ The mimetype parameters as dict. For example if the content
+ type is ``text/html; charset=utf-8`` the params would be
+ ``{'charset': 'utf-8'}``.
+
+ .. versionadded:: 0.5
+ ''')
+ location = header_property('Location', doc='''
+ The Location response-header field is used to redirect the recipient
+ to a location other than the Request-URI for completion of the request
+ or identification of a new resource.''')
+ age = header_property('Age', None, parse_date, http_date, doc='''
+ The Age response-header field conveys the sender's estimate of the
+ amount of time since the response (or its revalidation) was
+ generated at the origin server.
+
+ Age values are non-negative decimal integers, representing time in
+ seconds.''')
+ content_type = header_property('Content-Type', doc='''
+ The Content-Type entity-header field indicates the media type of the
+ entity-body sent to the recipient or, in the case of the HEAD method,
+ the media type that would have been sent had the request been a GET.
+ ''')
+ content_length = header_property('Content-Length', None, int, str, doc='''
+ The Content-Length entity-header field indicates the size of the
+ entity-body, in decimal number of OCTETs, sent to the recipient or,
+ in the case of the HEAD method, the size of the entity-body that would
+ have been sent had the request been a GET.''')
+ content_location = header_property('Content-Location', doc='''
+ The Content-Location entity-header field MAY be used to supply the
+ resource location for the entity enclosed in the message when that
+ entity is accessible from a location separate from the requested
+ resource's URI.''')
+ content_encoding = header_property('Content-Encoding', doc='''
+ The Content-Encoding entity-header field is used as a modifier to the
+ media-type. When present, its value indicates what additional content
+ codings have been applied to the entity-body, and thus what decoding
+ mechanisms must be applied in order to obtain the media-type
+ referenced by the Content-Type header field.''')
+ content_md5 = header_property('Content-MD5', doc='''
+ The Content-MD5 entity-header field, as defined in RFC 1864, is an
+ MD5 digest of the entity-body for the purpose of providing an
+ end-to-end message integrity check (MIC) of the entity-body. (Note:
+ a MIC is good for detecting accidental modification of the
+ entity-body in transit, but is not proof against malicious attacks.)
+ ''')
+ date = header_property('Date', None, parse_date, http_date, doc='''
+ The Date general-header field represents the date and time at which
+ the message was originated, having the same semantics as orig-date
+ in RFC 822.''')
+ expires = header_property('Expires', None, parse_date, http_date, doc='''
+ The Expires entity-header field gives the date/time after which the
+ response is considered stale. A stale cache entry may not normally be
+ returned by a cache.''')
+ last_modified = header_property('Last-Modified', None, parse_date,
+ http_date, doc='''
+ The Last-Modified entity-header field indicates the date and time at
+ which the origin server believes the variant was last modified.''')
+
+ def _get_retry_after(self):
+ value = self.headers.get('retry-after')
+ if value is None:
+ return
+ elif value.isdigit():
+ return datetime.utcnow() + timedelta(seconds=int(value))
+ return parse_date(value)
+ def _set_retry_after(self, value):
+ if value is None:
+ if 'retry-after' in self.headers:
+ del self.headers['retry-after']
+ return
+ elif isinstance(value, datetime):
+ value = http_date(value)
+ else:
+ value = str(value)
+ self.headers['Retry-After'] = value
+
+ retry_after = property(_get_retry_after, _set_retry_after, doc='''
+ The Retry-After response-header field can be used with a 503 (Service
+ Unavailable) response to indicate how long the service is expected
+ to be unavailable to the requesting client.
+
+ Time in seconds until expiration or date.''')
+
+ def _set_property(name, doc=None):
+ def fget(self):
+ def on_update(header_set):
+ if not header_set and name in self.headers:
+ del self.headers[name]
+ elif header_set:
+ self.headers[name] = header_set.to_header()
+ return parse_set_header(self.headers.get(name), on_update)
+ def fset(self, value):
+ if not value:
+ del self.headers[name]
+ elif isinstance(value, basestring):
+ self.headers[name] = value
+ else:
+ self.headers[name] = dump_header(value)
+ return property(fget, fset, doc=doc)
+
+ vary = _set_property('Vary', doc='''
+ The Vary field value indicates the set of request-header fields that
+ fully determines, while the response is fresh, whether a cache is
+ permitted to use the response to reply to a subsequent request
+ without revalidation.''')
+ content_language = _set_property('Content-Language', doc='''
+ The Content-Language entity-header field describes the natural
+ language(s) of the intended audience for the enclosed entity. Note
+ that this might not be equivalent to all the languages used within
+ the entity-body.''')
+ allow = _set_property('Allow', doc='''
+ The Allow entity-header field lists the set of methods supported
+ by the resource identified by the Request-URI. The purpose of this
+ field is strictly to inform the recipient of valid methods
+ associated with the resource. An Allow header field MUST be
+ present in a 405 (Method Not Allowed) response.''')
+
+ del _set_property, _get_mimetype, _set_mimetype, _get_retry_after, \
+ _set_retry_after
+
+
+class WWWAuthenticateMixin(object):
+ """Adds a :attr:`www_authenticate` property to a response object."""
+
+ @property
+ def www_authenticate(self):
+ """The `WWW-Authenticate` header in a parsed form."""
+ def on_update(www_auth):
+ if not www_auth and 'www-authenticate' in self.headers:
+ del self.headers['www-authenticate']
+ elif www_auth:
+ self.headers['WWW-Authenticate'] = www_auth.to_header()
+ header = self.headers.get('www-authenticate')
+ return parse_www_authenticate_header(header, on_update)
+
+
+class Request(BaseRequest, AcceptMixin, ETagRequestMixin,
+ UserAgentMixin, AuthorizationMixin,
+ CommonRequestDescriptorsMixin):
+ """Full featured request object implementing the following mixins:
+
+ - :class:`AcceptMixin` for accept header parsing
+ - :class:`ETagRequestMixin` for etag and cache control handling
+ - :class:`UserAgentMixin` for user agent introspection
+ - :class:`AuthorizationMixin` for http auth handling
+ - :class:`CommonRequestDescriptorsMixin` for common headers
+ """
+
+
+class Response(BaseResponse, ETagResponseMixin, ResponseStreamMixin,
+ CommonResponseDescriptorsMixin,
+ WWWAuthenticateMixin):
+ """Full featured response object implementing the following mixins:
+
+ - :class:`ETagResponseMixin` for etag and cache control handling
+ - :class:`ResponseStreamMixin` to add support for the `stream` property
+ - :class:`CommonResponseDescriptorsMixin` for various HTTP descriptors
+ - :class:`WWWAuthenticateMixin` for HTTP authentication support
+ """
diff --git a/websdk/werkzeug/wsgi.py b/websdk/werkzeug/wsgi.py
new file mode 100644
index 0000000..6d7f6ea
--- /dev/null
+++ b/websdk/werkzeug/wsgi.py
@@ -0,0 +1,830 @@
+# -*- coding: utf-8 -*-
+"""
+ werkzeug.wsgi
+ ~~~~~~~~~~~~~
+
+ This module implements WSGI related helpers.
+
+ :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+import re
+import os
+import urllib
+import urlparse
+import posixpath
+import mimetypes
+from itertools import chain
+from zlib import adler32
+from time import time, mktime
+from datetime import datetime
+
+from werkzeug._internal import _patch_wrapper
+from werkzeug.http import is_resource_modified, http_date
+
+
+def responder(f):
+ """Marks a function as responder. Decorate a function with it and it
+ will automatically call the return value as WSGI application.
+
+ Example::
+
+ @responder
+ def application(environ, start_response):
+ return Response('Hello World!')
+ """
+ return _patch_wrapper(f, lambda *a: f(*a)(*a[-2:]))
+
+
+def get_current_url(environ, root_only=False, strip_querystring=False,
+ host_only=False):
+ """A handy helper function that recreates the full URL for the current
+ request or parts of it. Here an example:
+
+ >>> from werkzeug.test import create_environ
+ >>> env = create_environ("/?param=foo", "http://localhost/script")
+ >>> get_current_url(env)
+ 'http://localhost/script/?param=foo'
+ >>> get_current_url(env, root_only=True)
+ 'http://localhost/script/'
+ >>> get_current_url(env, host_only=True)
+ 'http://localhost/'
+ >>> get_current_url(env, strip_querystring=True)
+ 'http://localhost/script/'
+
+ :param environ: the WSGI environment to get the current URL from.
+ :param root_only: set `True` if you only want the root URL.
+ :param strip_querystring: set to `True` if you don't want the querystring.
+ :param host_only: set to `True` if the host URL should be returned.
+ """
+ tmp = [environ['wsgi.url_scheme'], '://', get_host(environ)]
+ cat = tmp.append
+ if host_only:
+ return ''.join(tmp) + '/'
+ cat(urllib.quote(environ.get('SCRIPT_NAME', '').rstrip('/')))
+ if root_only:
+ cat('/')
+ else:
+ cat(urllib.quote('/' + environ.get('PATH_INFO', '').lstrip('/')))
+ if not strip_querystring:
+ qs = environ.get('QUERY_STRING')
+ if qs:
+ cat('?' + qs)
+ return ''.join(tmp)
+
+
+def get_host(environ):
+ """Return the real host for the given WSGI environment. This takes care
+ of the `X-Forwarded-Host` header.
+
+ :param environ: the WSGI environment to get the host of.
+ """
+ if 'HTTP_X_FORWARDED_HOST' in environ:
+ return environ['HTTP_X_FORWARDED_HOST']
+ elif 'HTTP_HOST' in environ:
+ return environ['HTTP_HOST']
+ result = environ['SERVER_NAME']
+ if (environ['wsgi.url_scheme'], environ['SERVER_PORT']) not \
+ in (('https', '443'), ('http', '80')):
+ result += ':' + environ['SERVER_PORT']
+ return result
+
+
+def pop_path_info(environ):
+ """Removes and returns the next segment of `PATH_INFO`, pushing it onto
+ `SCRIPT_NAME`. Returns `None` if there is nothing left on `PATH_INFO`.
+
+ If there are empty segments (``'/foo//bar``) these are ignored but
+ properly pushed to the `SCRIPT_NAME`:
+
+ >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'}
+ >>> pop_path_info(env)
+ 'a'
+ >>> env['SCRIPT_NAME']
+ '/foo/a'
+ >>> pop_path_info(env)
+ 'b'
+ >>> env['SCRIPT_NAME']
+ '/foo/a/b'
+
+ .. versionadded:: 0.5
+
+ :param environ: the WSGI environment that is modified.
+ """
+ path = environ.get('PATH_INFO')
+ if not path:
+ return None
+
+ script_name = environ.get('SCRIPT_NAME', '')
+
+ # shift multiple leading slashes over
+ old_path = path
+ path = path.lstrip('/')
+ if path != old_path:
+ script_name += '/' * (len(old_path) - len(path))
+
+ if '/' not in path:
+ environ['PATH_INFO'] = ''
+ environ['SCRIPT_NAME'] = script_name + path
+ return path
+
+ segment, path = path.split('/', 1)
+ environ['PATH_INFO'] = '/' + path
+ environ['SCRIPT_NAME'] = script_name + segment
+ return segment
+
+
+def peek_path_info(environ):
+ """Returns the next segment on the `PATH_INFO` or `None` if there
+ is none. Works like :func:`pop_path_info` without modifying the
+ environment:
+
+ >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'}
+ >>> peek_path_info(env)
+ 'a'
+ >>> peek_path_info(env)
+ 'a'
+
+ .. versionadded:: 0.5
+
+ :param environ: the WSGI environment that is checked.
+ """
+ segments = environ.get('PATH_INFO', '').lstrip('/').split('/', 1)
+ if segments:
+ return segments[0]
+
+
+def extract_path_info(environ_or_baseurl, path_or_url, charset='utf-8',
+ errors='replace', collapse_http_schemes=True):
+ """Extracts the path info from the given URL (or WSGI environment) and
+ path. The path info returned is a unicode string, not a bytestring
+ suitable for a WSGI environment. The URLs might also be IRIs.
+
+ If the path info could not be determined, `None` is returned.
+
+ Some examples:
+
+ >>> extract_path_info('http://example.com/app', '/app/hello')
+ u'/hello'
+ >>> extract_path_info('http://example.com/app',
+ ... 'https://example.com/app/hello')
+ u'/hello'
+ >>> extract_path_info('http://example.com/app',
+ ... 'https://example.com/app/hello',
+ ... collapse_http_schemes=False) is None
+ True
+
+ Instead of providing a base URL you can also pass a WSGI environment.
+
+ .. versionadded:: 0.6
+
+ :param environ_or_baseurl: a WSGI environment dict, a base URL or
+ base IRI. This is the root of the
+ application.
+ :param path_or_url: an absolute path from the server root, a
+ relative path (in which case it's the path info)
+ or a full URL. Also accepts IRIs and unicode
+ parameters.
+ :param charset: the charset for byte data in URLs
+ :param errors: the error handling on decode
+ :param collapse_http_schemes: if set to `False` the algorithm does
+ not assume that http and https on the
+ same server point to the same
+ resource.
+ """
+ from werkzeug.urls import uri_to_iri, url_fix
+
+ def _as_iri(obj):
+ if not isinstance(obj, unicode):
+ return uri_to_iri(obj, charset, errors)
+ return obj
+
+ def _normalize_netloc(scheme, netloc):
+ parts = netloc.split(u'@', 1)[-1].split(u':', 1)
+ if len(parts) == 2:
+ netloc, port = parts
+ if (scheme == u'http' and port == u'80') or \
+ (scheme == u'https' and port == u'443'):
+ port = None
+ else:
+ netloc = parts[0]
+ port = None
+ if port is not None:
+ netloc += u':' + port
+ return netloc
+
+ # make sure whatever we are working on is a IRI and parse it
+ path = _as_iri(path_or_url)
+ if isinstance(environ_or_baseurl, dict):
+ environ_or_baseurl = get_current_url(environ_or_baseurl,
+ root_only=True)
+ base_iri = _as_iri(environ_or_baseurl)
+ base_scheme, base_netloc, base_path, = \
+ urlparse.urlsplit(base_iri)[:3]
+ cur_scheme, cur_netloc, cur_path, = \
+ urlparse.urlsplit(urlparse.urljoin(base_iri, path))[:3]
+
+ # normalize the network location
+ base_netloc = _normalize_netloc(base_scheme, base_netloc)
+ cur_netloc = _normalize_netloc(cur_scheme, cur_netloc)
+
+ # is that IRI even on a known HTTP scheme?
+ if collapse_http_schemes:
+ for scheme in base_scheme, cur_scheme:
+ if scheme not in (u'http', u'https'):
+ return None
+ else:
+ if not (base_scheme in (u'http', u'https') and \
+ base_scheme == cur_scheme):
+ return None
+
+ # are the netlocs compatible?
+ if base_netloc != cur_netloc:
+ return None
+
+ # are we below the application path?
+ base_path = base_path.rstrip(u'/')
+ if not cur_path.startswith(base_path):
+ return None
+
+ return u'/' + cur_path[len(base_path):].lstrip(u'/')
+
+
+class SharedDataMiddleware(object):
+ """A WSGI middleware that provides static content for development
+ environments or simple server setups. Usage is quite simple::
+
+ import os
+ from werkzeug.wsgi import SharedDataMiddleware
+
+ app = SharedDataMiddleware(app, {
+ '/shared': os.path.join(os.path.dirname(__file__), 'shared')
+ })
+
+ The contents of the folder ``./shared`` will now be available on
+ ``http://example.com/shared/``. This is pretty useful during development
+ because a standalone media server is not required. One can also mount
+ files on the root folder and still continue to use the application because
+ the shared data middleware forwards all unhandled requests to the
+ application, even if the requests are below one of the shared folders.
+
+ If `pkg_resources` is available you can also tell the middleware to serve
+ files from package data::
+
+ app = SharedDataMiddleware(app, {
+ '/shared': ('myapplication', 'shared_files')
+ })
+
+ This will then serve the ``shared_files`` folder in the `myapplication`
+ Python package.
+
+ The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch`
+ rules for files that are not accessible from the web. If `cache` is set to
+ `False` no caching headers are sent.
+
+ Currently the middleware does not support non ASCII filenames. If the
+ encoding on the file system happens to be the encoding of the URI it may
+ work but this could also be by accident. We strongly suggest using ASCII
+ only file names for static files.
+
+ The middleware will guess the mimetype using the Python `mimetype`
+ module. If it's unable to figure out the charset it will fall back
+ to `fallback_mimetype`.
+
+ .. versionchanged:: 0.5
+ The cache timeout is configurable now.
+
+ .. versionadded:: 0.6
+ The `fallback_mimetype` parameter was added.
+
+ :param app: the application to wrap. If you don't want to wrap an
+ application you can pass it :exc:`NotFound`.
+ :param exports: a dict of exported files and folders.
+ :param disallow: a list of :func:`~fnmatch.fnmatch` rules.
+ :param fallback_mimetype: the fallback mimetype for unknown files.
+ :param cache: enable or disable caching headers.
+ :Param cache_timeout: the cache timeout in seconds for the headers.
+ """
+
+ def __init__(self, app, exports, disallow=None, cache=True,
+ cache_timeout=60 * 60 * 12, fallback_mimetype='text/plain'):
+ self.app = app
+ self.exports = {}
+ self.cache = cache
+ self.cache_timeout = cache_timeout
+ for key, value in exports.iteritems():
+ if isinstance(value, tuple):
+ loader = self.get_package_loader(*value)
+ elif isinstance(value, basestring):
+ if os.path.isfile(value):
+ loader = self.get_file_loader(value)
+ else:
+ loader = self.get_directory_loader(value)
+ else:
+ raise TypeError('unknown def %r' % value)
+ self.exports[key] = loader
+ if disallow is not None:
+ from fnmatch import fnmatch
+ self.is_allowed = lambda x: not fnmatch(x, disallow)
+ self.fallback_mimetype = fallback_mimetype
+
+ def is_allowed(self, filename):
+ """Subclasses can override this method to disallow the access to
+ certain files. However by providing `disallow` in the constructor
+ this method is overwritten.
+ """
+ return True
+
+ def _opener(self, filename):
+ return lambda: (
+ open(filename, 'rb'),
+ datetime.utcfromtimestamp(os.path.getmtime(filename)),
+ int(os.path.getsize(filename))
+ )
+
+ def get_file_loader(self, filename):
+ return lambda x: (os.path.basename(filename), self._opener(filename))
+
+ def get_package_loader(self, package, package_path):
+ from pkg_resources import DefaultProvider, ResourceManager, \
+ get_provider
+ loadtime = datetime.utcnow()
+ provider = get_provider(package)
+ manager = ResourceManager()
+ filesystem_bound = isinstance(provider, DefaultProvider)
+ def loader(path):
+ if path is None:
+ return None, None
+ path = posixpath.join(package_path, path)
+ if not provider.has_resource(path):
+ return None, None
+ basename = posixpath.basename(path)
+ if filesystem_bound:
+ return basename, self._opener(
+ provider.get_resource_filename(manager, path))
+ return basename, lambda: (
+ provider.get_resource_stream(manager, path),
+ loadtime,
+ 0
+ )
+ return loader
+
+ def get_directory_loader(self, directory):
+ def loader(path):
+ if path is not None:
+ path = os.path.join(directory, path)
+ else:
+ path = directory
+ if os.path.isfile(path):
+ return os.path.basename(path), self._opener(path)
+ return None, None
+ return loader
+
+ def generate_etag(self, mtime, file_size, real_filename):
+ return 'wzsdm-%d-%s-%s' % (
+ mktime(mtime.timetuple()),
+ file_size,
+ adler32(real_filename) & 0xffffffff
+ )
+
+ def __call__(self, environ, start_response):
+ # sanitize the path for non unix systems
+ cleaned_path = environ.get('PATH_INFO', '').strip('/')
+ for sep in os.sep, os.altsep:
+ if sep and sep != '/':
+ cleaned_path = cleaned_path.replace(sep, '/')
+ path = '/'.join([''] + [x for x in cleaned_path.split('/')
+ if x and x != '..'])
+ file_loader = None
+ for search_path, loader in self.exports.iteritems():
+ if search_path == path:
+ real_filename, file_loader = loader(None)
+ if file_loader is not None:
+ break
+ if not search_path.endswith('/'):
+ search_path += '/'
+ if path.startswith(search_path):
+ real_filename, file_loader = loader(path[len(search_path):])
+ if file_loader is not None:
+ break
+ if file_loader is None or not self.is_allowed(real_filename):
+ return self.app(environ, start_response)
+
+ guessed_type = mimetypes.guess_type(real_filename)
+ mime_type = guessed_type[0] or self.fallback_mimetype
+ f, mtime, file_size = file_loader()
+
+ headers = [('Date', http_date())]
+ if self.cache:
+ timeout = self.cache_timeout
+ etag = self.generate_etag(mtime, file_size, real_filename)
+ headers += [
+ ('Etag', '"%s"' % etag),
+ ('Cache-Control', 'max-age=%d, public' % timeout)
+ ]
+ if not is_resource_modified(environ, etag, last_modified=mtime):
+ f.close()
+ start_response('304 Not Modified', headers)
+ return []
+ headers.append(('Expires', http_date(time() + timeout)))
+ else:
+ headers.append(('Cache-Control', 'public'))
+
+ headers.extend((
+ ('Content-Type', mime_type),
+ ('Content-Length', str(file_size)),
+ ('Last-Modified', http_date(mtime))
+ ))
+ start_response('200 OK', headers)
+ return wrap_file(environ, f)
+
+
+class DispatcherMiddleware(object):
+ """Allows one to mount middlewares or applications in a WSGI application.
+ This is useful if you want to combine multiple WSGI applications::
+
+ app = DispatcherMiddleware(app, {
+ '/app2': app2,
+ '/app3': app3
+ })
+ """
+
+ def __init__(self, app, mounts=None):
+ self.app = app
+ self.mounts = mounts or {}
+
+ def __call__(self, environ, start_response):
+ script = environ.get('PATH_INFO', '')
+ path_info = ''
+ while '/' in script:
+ if script in self.mounts:
+ app = self.mounts[script]
+ break
+ items = script.split('/')
+ script = '/'.join(items[:-1])
+ path_info = '/%s%s' % (items[-1], path_info)
+ else:
+ app = self.mounts.get(script, self.app)
+ original_script_name = environ.get('SCRIPT_NAME', '')
+ environ['SCRIPT_NAME'] = original_script_name + script
+ environ['PATH_INFO'] = path_info
+ return app(environ, start_response)
+
+
+class ClosingIterator(object):
+ """The WSGI specification requires that all middlewares and gateways
+ respect the `close` callback of an iterator. Because it is useful to add
+ another close action to a returned iterator and adding a custom iterator
+ is a boring task this class can be used for that::
+
+ return ClosingIterator(app(environ, start_response), [cleanup_session,
+ cleanup_locals])
+
+ If there is just one close function it can be passed instead of the list.
+
+ A closing iterator is not needed if the application uses response objects
+ and finishes the processing if the response is started::
+
+ try:
+ return response(environ, start_response)
+ finally:
+ cleanup_session()
+ cleanup_locals()
+ """
+
+ def __init__(self, iterable, callbacks=None):
+ iterator = iter(iterable)
+ self._next = iterator.next
+ if callbacks is None:
+ callbacks = []
+ elif callable(callbacks):
+ callbacks = [callbacks]
+ else:
+ callbacks = list(callbacks)
+ iterable_close = getattr(iterator, 'close', None)
+ if iterable_close:
+ callbacks.insert(0, iterable_close)
+ self._callbacks = callbacks
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ return self._next()
+
+ def close(self):
+ for callback in self._callbacks:
+ callback()
+
+
+def wrap_file(environ, file, buffer_size=8192):
+ """Wraps a file. This uses the WSGI server's file wrapper if available
+ or otherwise the generic :class:`FileWrapper`.
+
+ .. versionadded:: 0.5
+
+ If the file wrapper from the WSGI server is used it's important to not
+ iterate over it from inside the application but to pass it through
+ unchanged. If you want to pass out a file wrapper inside a response
+ object you have to set :attr:`~BaseResponse.direct_passthrough` to `True`.
+
+ More information about file wrappers are available in :pep:`333`.
+
+ :param file: a :class:`file`-like object with a :meth:`~file.read` method.
+ :param buffer_size: number of bytes for one iteration.
+ """
+ return environ.get('wsgi.file_wrapper', FileWrapper)(file, buffer_size)
+
+
+class FileWrapper(object):
+ """This class can be used to convert a :class:`file`-like object into
+ an iterable. It yields `buffer_size` blocks until the file is fully
+ read.
+
+ You should not use this class directly but rather use the
+ :func:`wrap_file` function that uses the WSGI server's file wrapper
+ support if it's available.
+
+ .. versionadded:: 0.5
+
+ If you're using this object together with a :class:`BaseResponse` you have
+ to use the `direct_passthrough` mode.
+
+ :param file: a :class:`file`-like object with a :meth:`~file.read` method.
+ :param buffer_size: number of bytes for one iteration.
+ """
+
+ def __init__(self, file, buffer_size=8192):
+ self.file = file
+ self.buffer_size = buffer_size
+
+ def close(self):
+ if hasattr(self.file, 'close'):
+ self.file.close()
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ data = self.file.read(self.buffer_size)
+ if data:
+ return data
+ raise StopIteration()
+
+
+def make_limited_stream(stream, limit):
+ """Makes a stream limited."""
+ if not isinstance(stream, LimitedStream):
+ if limit is None:
+ raise TypeError('stream not limited and no limit provided.')
+ stream = LimitedStream(stream, limit)
+ return stream
+
+
+def make_line_iter(stream, limit=None, buffer_size=10 * 1024):
+ """Safely iterates line-based over an input stream. If the input stream
+ is not a :class:`LimitedStream` the `limit` parameter is mandatory.
+
+ This uses the stream's :meth:`~file.read` method internally as opposite
+ to the :meth:`~file.readline` method that is unsafe and can only be used
+ in violation of the WSGI specification. The same problem applies to the
+ `__iter__` function of the input stream which calls :meth:`~file.readline`
+ without arguments.
+
+ If you need line-by-line processing it's strongly recommended to iterate
+ over the input stream using this helper function.
+
+ .. versionchanged:: 0.8
+ This function now ensures that the limit was reached.
+
+ :param stream: the stream to iterate over.
+ :param limit: the limit in bytes for the stream. (Usually
+ content length. Not necessary if the `stream`
+ is a :class:`LimitedStream`.
+ :param buffer_size: The optional buffer size.
+ """
+ stream = make_limited_stream(stream, limit)
+ _read = stream.read
+ buffer = []
+ while 1:
+ if len(buffer) > 1:
+ yield buffer.pop()
+ continue
+
+ # we reverse the chunks because popping from the last
+ # position of the list is O(1) and the number of chunks
+ # read will be quite large for binary files.
+ chunks = _read(buffer_size).splitlines(True)
+ chunks.reverse()
+
+ first_chunk = buffer and buffer[0] or ''
+ if chunks:
+ if first_chunk.endswith('\n') or first_chunk.endswith('\r'):
+ yield first_chunk
+ first_chunk = ''
+ first_chunk += chunks.pop()
+ if not first_chunk:
+ return
+
+ buffer = chunks
+ yield first_chunk
+
+
+def make_chunk_iter(stream, separator, limit=None, buffer_size=10 * 1024):
+ """Works like :func:`make_line_iter` but accepts a separator
+ which divides chunks. If you want newline based processing
+ you shuold use :func:`make_limited_stream` instead as it
+ supports arbitrary newline markers.
+
+ .. versionadded:: 0.8
+
+ :param stream: the stream to iterate over.
+ :param separator: the separator that divides chunks.
+ :param limit: the limit in bytes for the stream. (Usually
+ content length. Not necessary if the `stream`
+ is a :class:`LimitedStream`.
+ :param buffer_size: The optional buffer size.
+ """
+ stream = make_limited_stream(stream, limit)
+ _read = stream.read
+ _split = re.compile(r'(%s)' % re.escape(separator)).split
+ buffer = []
+ while 1:
+ new_data = _read(buffer_size)
+ if not new_data:
+ break
+ chunks = _split(new_data)
+ new_buf = []
+ for item in chain(buffer, chunks):
+ if item == separator:
+ yield ''.join(new_buf)
+ new_buf = []
+ else:
+ new_buf.append(item)
+ buffer = new_buf
+ if buffer:
+ yield ''.join(buffer)
+
+
+class LimitedStream(object):
+ """Wraps a stream so that it doesn't read more than n bytes. If the
+ stream is exhausted and the caller tries to get more bytes from it
+ :func:`on_exhausted` is called which by default returns an empty
+ string. The return value of that function is forwarded
+ to the reader function. So if it returns an empty string
+ :meth:`read` will return an empty string as well.
+
+ The limit however must never be higher than what the stream can
+ output. Otherwise :meth:`readlines` will try to read past the
+ limit.
+
+ The `silent` parameter has no effect if :meth:`is_exhausted` is
+ overriden by a subclass.
+
+ .. versionchanged:: 0.6
+ Non-silent usage was deprecated because it causes confusion.
+ If you want that, override :meth:`is_exhausted` and raise a
+ :exc:`~exceptions.BadRequest` yourself.
+
+ .. admonition:: Note on WSGI compliance
+
+ calls to :meth:`readline` and :meth:`readlines` are not
+ WSGI compliant because it passes a size argument to the
+ readline methods. Unfortunately the WSGI PEP is not safely
+ implementable without a size argument to :meth:`readline`
+ because there is no EOF marker in the stream. As a result
+ of that the use of :meth:`readline` is discouraged.
+
+ For the same reason iterating over the :class:`LimitedStream`
+ is not portable. It internally calls :meth:`readline`.
+
+ We strongly suggest using :meth:`read` only or using the
+ :func:`make_line_iter` which safely iterates line-based
+ over a WSGI input stream.
+
+ :param stream: the stream to wrap.
+ :param limit: the limit for the stream, must not be longer than
+ what the string can provide if the stream does not
+ end with `EOF` (like `wsgi.input`)
+ :param silent: If set to `True` the stream will allow reading
+ past the limit and will return an empty string.
+ """
+
+ def __init__(self, stream, limit, silent=True):
+ self._read = stream.read
+ self._readline = stream.readline
+ self._pos = 0
+ self.limit = limit
+ self.silent = silent
+ if not silent:
+ from warnings import warn
+ warn(DeprecationWarning('non-silent usage of the '
+ 'LimitedStream is deprecated. If you want to '
+ 'continue to use the stream in non-silent usage '
+ 'override on_exhausted.'), stacklevel=2)
+
+ def __iter__(self):
+ return self
+
+ @property
+ def is_exhausted(self):
+ """If the stream is exhausted this attribute is `True`."""
+ return self._pos >= self.limit
+
+ def on_exhausted(self):
+ """This is called when the stream tries to read past the limit.
+ The return value of this function is returned from the reading
+ function.
+ """
+ if self.silent:
+ return ''
+ from werkzeug.exceptions import BadRequest
+ raise BadRequest('input stream exhausted')
+
+ def on_disconnect(self):
+ """What should happen if a disconnect is detected? The return
+ value of this function is returned from read functions in case
+ the client went away. By default a
+ :exc:`~werkzeug.exceptions.ClientDisconnected` exception is raised.
+ """
+ from werkzeug.exceptions import ClientDisconnected
+ raise ClientDisconnected()
+
+ def exhaust(self, chunk_size=1024 * 16):
+ """Exhaust the stream. This consumes all the data left until the
+ limit is reached.
+
+ :param chunk_size: the size for a chunk. It will read the chunk
+ until the stream is exhausted and throw away
+ the results.
+ """
+ to_read = self.limit - self._pos
+ chunk = chunk_size
+ while to_read > 0:
+ chunk = min(to_read, chunk)
+ self.read(chunk)
+ to_read -= chunk
+
+ def read(self, size=None):
+ """Read `size` bytes or if size is not provided everything is read.
+
+ :param size: the number of bytes read.
+ """
+ if self._pos >= self.limit:
+ return self.on_exhausted()
+ if size is None or size == -1: # -1 is for consistence with file
+ size = self.limit
+ to_read = min(self.limit - self._pos, size)
+ try:
+ read = self._read(to_read)
+ except (IOError, ValueError):
+ return self.on_disconnect()
+ if to_read and len(read) != to_read:
+ return self.on_disconnect()
+ self._pos += len(read)
+ return read
+
+ def readline(self, size=None):
+ """Reads one line from the stream."""
+ if self._pos >= self.limit:
+ return self.on_exhausted()
+ if size is None:
+ size = self.limit - self._pos
+ else:
+ size = min(size, self.limit - self._pos)
+ try:
+ line = self._readline(size)
+ except (ValueError, IOError):
+ return self.on_disconnect()
+ if size and not line:
+ return self.on_disconnect()
+ self._pos += len(line)
+ return line
+
+ def readlines(self, size=None):
+ """Reads a file into a list of strings. It calls :meth:`readline`
+ until the file is read to the end. It does support the optional
+ `size` argument if the underlaying stream supports it for
+ `readline`.
+ """
+ last_pos = self._pos
+ result = []
+ if size is not None:
+ end = min(self.limit, last_pos + size)
+ else:
+ end = self.limit
+ while 1:
+ if size is not None:
+ size -= last_pos - self._pos
+ if self._pos >= end:
+ break
+ result.append(self.readline(size))
+ if size is not None:
+ last_pos = self._pos
+ return result
+
+ def next(self):
+ line = self.readline()
+ if line is None:
+ raise StopIteration()
+ return line