[
  {
    "path": ".gitignore",
    "content": "build/*\ndocs/_build/*\ndev_server/build/*\ndev_server/sae_python_dev.egg-info/*\n*~\n*.DS_Store\n*.default\n*.types\n*.pyc\n*.egg\n*.pdf\n*.bak\n*.doc\n*.ppt\n*.xls\n*.mht\n*.mp3\n*.mp4\n*.wma\n*.rar\n*.zip\n*.7z\n*.xz\n*.map\n*.log\n*.txt\n*.swp\n*.gz\n*.ko\n*.so\n.tmp*\n.svn\n.cvs\n"
  },
  {
    "path": "README",
    "content": "Copyright © 2011 新浪网研发中心. All rights reserved.\n\nSAE Python Team\n"
  },
  {
    "path": "dev_server/README",
    "content": "SAE Python development server - experimental\n\nAll Rights Reserved 2011\nSAE Python Team\n\n目前支持的服务包括：mysql, taskqueue, memcache, storage, mail。\n大部分的服务直接运行dev_server.py进行调试就可以了，部分服务需要做一些配置。\n\n注意： 本工具仅为应用开发便利之用，对sae python环境的模拟并不完整。\n\nInstall\n--------------\n\n    sudo python setup.py install\n\n基本使用\n------------\n\n使用svn检出app代码之后，建立以数字为标识的发布目录，切换到发布目录:\n\n    $ pwd\n    /home/jaime/source/blackfire/1\n\n编辑index.wsgi和config.yaml:\n\n    $ vi index.wsgi\n    import sae\n\n    def app(environ, start_response):\n        status = '200 OK'\n        response_headers = [('Content-type', 'text/plain')]\n        start_response(status, response_headers)\n        return ['Hello, world! reloading test3']\n\n    application = sae.create_wsgi_app(app)\n\n    $ vi config.yaml\n    ---\n    name: blackfire\n    version: 1\n    ...\n\n运行dev_server.py:\n\n    $ dev_server.py \n    MySQL config not found: app.py\n    Start development server on http://localhost:8080/\n\n访问 http://localhost:8080 端口就可以看到Hello, world!了。\n\n使用MySQL服务\n----------------\n\n配置好MySQL本地开发server，使用 `--mysql` 参数运行dev_server.py。\n\n    $ dev_server.py --mysql=user:password@host:port\n\n现在你可以在应用代码中像在SAE线上环境一样使用MySQL服务了。dev_server.py默认使用\n名为 `app_应用名` 的数据库。\n\n使用storage服务\n---------------\n\n使用 `--storage-path` 参数运行dev_server.py。\n\n    $ dev_server.py --storage-path=/path/to/local/storage/data\n\n本地的storage服务使用以下的目录结构来模拟线上的storage。\n\n    storage-path/\n          domain1/\n                key1\n                key2\n          domain2/\n          domain3/\n\n--storage-path配置的路径下每个子文件夹会映射为storage中的一个domain，而每个子文\n件夹下的文件映射为domain下的一个key，其内容为对应key的数据。\n\n.. note: \n\n    为方便调试，dev_server自带的sae.storage在某个domain不存在的情况下会自动创建\n    该domain。线上环境中的domain需要在sae后台面板中手动创建。\n\n使用pylibmc\n--------------\n\ndev_server自带了一个dummy pylibmc，所以无须安装pylibmc就可以直接使用memcache服务\n了。该模块将所有的数据存贮在内存中，dev_server.py进程结束时，所有的数据都会丢失\n。\n\n使用kvdb\n----------------\n\nkvdb默认数据存在内存中，dev_server.py进程结束时，数据会全部丢失，如果需要保存数据，\n请使用如下命令行启动dev_server.py \n\n    $ dev_server.py --kvdb-file=/path/to/kvdb/local/file\n"
  },
  {
    "path": "dev_server/bundle_local.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"Create a local bundle for your virtualenv environment\n\nExport all the packages listed in requirements.txt to ./virtualenv.bundle\ndirectory\n\nThen you can create a .zip file before uploading them to sae\n\nUsage: bundle_local -r requirements.txt\n\n\"\"\"\n\nfrom optparse import OptionParser\nimport os\nimport sys\nimport pip.util\nimport shutil\n\nTMP = 'virtualenv.bundle'\n\nZIP_FILE = TMP + '.zip'\n\ndef main():\n    parser = OptionParser()\n    parser.add_option(\"-r\", dest=\"requirements\",\n                      help=\"The requirements.txt file outputed by pip freeze\")\n    (options, args) = parser.parse_args()\n\n    if not options.requirements:\n        print 'requirements.txt not found'\n        sys.exit(-1)\n\n    if os.path.exists(TMP):\n        shutil.rmtree(TMP)\n    os.mkdir(TMP)\n\n    shutil.copy2(options.requirements, os.path.join(TMP, 'requirements.txt'))\n\n    # Get all installed packages on system\n    installed_dists = {}\n    for dist in pip.util.get_installed_distributions():\n        installed_dists[dist.project_name] = dist\n\n    # Get the dists in requirements.txt\n    dists = []\n    for line in open(options.requirements, 'r').readlines():\n        if line.strip() or line.startswith('#'):\n            pass\n        pkg = line.split('==')[0]\n\n        if pkg not in installed_dists:\n            raise Exception('%s not installed' % pkg)\n        dists.append(installed_dists[pkg])\n\n    top_levels = []\n    for dist in dists:\n        mods = [(dist.location, mod) for mod in dist.get_metadata('top_level.txt').splitlines()]\n        top_levels += mods\n\n    top_levels = list(set(top_levels))\n    copy_modules(top_levels, TMP)\n\n\ndef copy_modules(mod_paths, dest):\n    for loc, mod in mod_paths:\n        if os.path.isdir(loc):\n            src = os.path.join(loc, mod)\n            if os.path.isdir(src):\n                shutil.copytree(src, os.path.join(dest, mod), ignore=shutil.ignore_patterns('*.pyc'))\n            else:\n                # Single file module\n                shutil.copy2(src + '.py', dest)\n        else:\n            # Egg file ?\n            import zipfile\n            zf = zipfile.ZipFile(loc)\n            members = filter(lambda f: f.startswith(mod), zf.namelist())\n            for m in members:\n                zf.extract(m, dest)\n            zf.close()\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "dev_server/cloudsql.py",
    "content": "#!/usr/bin/env python\n\n# Copyright (C) 2012-2013 SINA, All rights reserved.\n\n\"\"\"Command line client for SAE MySQL Service. \"\"\"\n\nimport sys\nimport os\nimport logging\nimport optparse\n\nimport sae._restful_mysql\nimport sae._restful_mysql._mysql_exceptions\nsys.modules['_mysql_exceptions'] = sae._restful_mysql._mysql_exceptions\n\nfrom grizzled import db\nfrom grizzled.db import mysql\nimport prettytable\nimport sqlcmd\nfrom sqlcmd import config\n\nlogging.basicConfig(level=logging.WARNING)\nsqlcmd.log = logging.getLogger('cloudsql')\n\nsqlcmd.DEFAULT_CONFIG_DIR = os.path.expanduser('~/.saecloud')\nsqlcmd.RC_FILE = os.path.join(sqlcmd.DEFAULT_CONFIG_DIR, 'cloudsql.config')\nsqlcmd.HISTORY_FILE_FORMAT = os.path.join(sqlcmd.DEFAULT_CONFIG_DIR, '%s.hist')\nsqlcmd.INTRO = 'SAE MySQL Client\\n\\nType \"help\" or \"?\" for help.\\n'\n\nDEFAULT_ENCODING = 'utf-8'\nUSAGE = '%prog [options] database_name'\n\nDEFAULT_SAE_MYSQL_HOST = 'w.rdc.sae.sina.com.cn'\nDEFAULT_SAE_MYSQL_PORT = 3307\nDEFAULT_SAE_MYSQL_DB_PREFIX = 'app_'\n\nclass CloudSqlDriver(mysql.MySQLDriver):\n    \"\"\"Grizzled DB Driver for Cloud SAE MySQL Service.\"\"\"\n\n    NAME = 'cloudsql'\n\n    def get_import(self):\n        return sae._restful_mysql\n\n    def get_display_name(self):\n        return 'Cloud SQL'\n\n    def do_connect(self, host, port, user, password, database):\n        # Fix grizzled's mysql driver which omit the port argument when connecting.\n        dbi = self.get_import()\n        port = port and int(port) or 3306\n        return dbi.connect(host=host, user=user, passwd=password, db=database, port=port)\n\nclass CloudSqlCmd(sqlcmd.SQLCmd):\n    \"\"\"The SQLCmd command interpreter for Cloud SQL.\"\"\"\n\n    sqlcmd.SQLCmd.MAIN_PROMPT = 'mysql> '\n    sqlcmd.SQLCmd.CONTINUATION_PROMPT = '    -> '\n\n    sqlcmd.SQLCmd.NO_SEMI_NEEDED.update(\n        ['about', 'desc', 'describe', 'echo', 'exit', 'h', 'hist',\n         'history', 'load', 'run', 'r', 'redo', 'set', 'show', 'var', 'vars'])\n\n    for method in ['do_dot_connect', 'do_dot_desc', 'do_begin']:\n        delattr(sqlcmd.SQLCmd, method)\n\n    for cmd in ['show', 'describe', 'echo', 'load', 'run', 'exit', 'h',\n                'hist', 'history', 'var', 'vars', 'about']:\n        method = 'do_dot_' + cmd\n        setattr(sqlcmd.SQLCmd, method.replace('dot_', ''), getattr(\n            sqlcmd.SQLCmd, method))\n        delattr(sqlcmd.SQLCmd, method)\n        method = 'complete_dot_' + cmd\n        if hasattr(sqlcmd.SQLCmd, method):\n            setattr(sqlcmd.SQLCmd, method.replace('dot_', ''), getattr(\n                sqlcmd.SQLCmd, method))\n            delattr(sqlcmd.SQLCmd, method)\n\n    def do_redo(self, args):\n        # XXX: Fix global name 'do_r' is not defined problem in sqlcmd\n        self.do_r(args)\n\n    def _SQLCmd__set_setting(self, varname, value):\n        # XXX: Fix bool object has no lower attribute in sqlcmd\n        return sqlcmd.SQLCmd._SQLCmd__set_setting(self, varname, str(value))\n\n    def do_desc(self, args):\n        self.do_describe(args, cmd='.desc')\n    complete_desc = sqlcmd.SQLCmd.complete_dot_desc\n\n    def do_load(self, args):\n        self.do_run(args)\n\n    def preloop(self, *args, **kwargs):\n        sqlcmd.SQLCmd.preloop(self, *args, **kwargs)\n        # Just exit if the connect failed\n        if self._SQLCmd__db is None: sys.exit(1)\n\n    def __init__(self, *args, **kwargs):\n        sqlcmd.SQLCmd.__init__(self, *args, **kwargs)\n        self.prompt = sqlcmd.SQLCmd.MAIN_PROMPT\n        self.output_encoding = DEFAULT_ENCODING\n\n    def set_output_encoding(self, encoding):\n        self.output_encoding = encoding\n\n    def _build_table(self, cursor):\n        \"\"\"Builds an output PrettyTable from the results in the given cursor.\"\"\"\n        if not cursor.description:\n            return None\n\n        column_names = [column[0] for column in cursor.description]\n        table = prettytable.PrettyTable(column_names)\n        rows = cursor.fetchall()\n        if not rows:\n            return table\n        for i, col in enumerate(rows[0]):\n            table.align[column_names[i]] = isinstance(col, basestring) and 'l' or 'r'\n        for row in rows: table.add_row(row)\n        return table\n\n    def _SQLCmd__handle_select(self, args, cursor, command='select'):\n        \"\"\"Overrides SQLCmd.__handle_select to display output with prettytable.\"\"\"\n        self._SQLCmd__exec_SQL(cursor, command, args)\n        table = self._build_table(cursor)\n        if table:\n            output = table.get_string()\n            if isinstance(output, unicode):\n                print output.encode(self.output_encoding)\n            else:\n                print output\n\ndef _create_config_dir():\n    \"\"\"Creates the sqlcmd config directory if necessary.\"\"\"\n    directory = sqlcmd.DEFAULT_CONFIG_DIR\n    if not os.access(directory, os.R_OK | os.W_OK | os.X_OK):\n        old_umask = os.umask(077)\n        os.makedirs(sqlcmd.DEFAULT_CONFIG_DIR)\n        os.umask(old_umask)\n\ndef main(argv):\n    parser = optparse.OptionParser(usage=USAGE)\n    parser.add_option('-u', '--username', dest='username',\n                      help='MySQL username to use when connecting to the server.')\n    parser.add_option('-p', '--password', dest='password',\n                      help='MySQL password to use when connecting to the server.')\n    parser.add_option('-e', '--output_encoding', dest='output_encoding',\n                      default=DEFAULT_ENCODING,\n                      help='Output encoding. Defaults to %s.' % DEFAULT_ENCODING)\n\n    (options, args) = parser.parse_args(argv[1:])\n\n    if len(args) != 1:\n        parser.print_help(sys.stderr)\n        return 1\n\n    if not options.username or not options.password:\n        print >>sys.stderr, 'Error: username or password is missing.\\n'\n        return 1\n\n    if args[0].startswith(DEFAULT_SAE_MYSQL_DB_PREFIX):\n        database_name = args[0]\n    else:\n        database_name = DEFAULT_SAE_MYSQL_DB_PREFIX + args[0]\n    instance_alias = database_name\n\n    _create_config_dir()\n\n    db.add_driver(CloudSqlDriver.NAME, CloudSqlDriver)\n    sql_cmd_config = config.SQLCmdConfig(None)\n    sql_cmd_config.add('__cloudsql__', instance_alias,\n                       DEFAULT_SAE_MYSQL_HOST , DEFAULT_SAE_MYSQL_PORT, database_name,\n                       CloudSqlDriver.NAME, options.username, options.password)\n    sql_cmd = CloudSqlCmd(sql_cmd_config)\n    sql_cmd.set_output_encoding(options.output_encoding)\n    sql_cmd.set_database(instance_alias)\n    sql_cmd.cmdloop()\n\n    return 0\n\nif __name__ == '__main__':\n    sys.exit(main(sys.argv))\n"
  },
  {
    "path": "dev_server/dev_server.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"Simple development server\n\nMake sure you're use python 2.7 for developing\n\n\"\"\"\nimport sys\nimport os\nimport os.path\nimport re\nimport imp\nimport yaml\nfrom optparse import OptionParser\n\nfrom sae.util import search_file_bottom_up\n\nfrom sae.channel import _channel_wrapper\n\ndef setup_sae_environ(conf):\n    # Add dummy pylibmc module\n    import sae.memcache\n    sys.modules['pylibmc'] = sae.memcache\n\n    # Save kvdb data in this file else the data will lost\n    # when the dev_server.py is down\n    if conf.kvdb:\n        print 'KVDB: ', conf.kvdb\n        os.environ['sae.kvdb.file'] = conf.kvdb\n\n    # Add app_root to sys.path\n    cwd = os.getcwd()\n    if cwd not in sys.path:\n        sys.path.insert(0, cwd)\n\n    try:\n        appname = str(conf.name)\n        appversion = str(conf.version)\n    except AttributeError:\n        raise AttributeError('`name` or `version` not found in `config.yaml`')\n\n    if conf.mysql:\n        import sae.const\n\n        p = re.compile('^(.+):(.+)@(.+):(\\d+)$')\n        m = p.match(conf.mysql)\n        if not m:\n            raise Exception(\"Invalid mysql configuration\")\n\n        user, password, host, port = m.groups()\n        dbname = 'app_' + appname\n        sae.const.MYSQL_DB = dbname\n        sae.const.MYSQL_USER = user\n        sae.const.MYSQL_PASS = password\n        sae.const.MYSQL_PORT = port\n        sae.const.MYSQL_HOST = host\n        sae.const.MYSQL_HOST_S = host\n\n        print 'MySQL: %s.%s' % (conf.mysql, dbname)\n    else:\n        print 'MySQL config not found'\n\n    if conf.storage:\n        os.environ['sae.storage.path'] = os.path.abspath(conf.storage)\n        \n    # Add custom environment variable\n    os.environ['HTTP_HOST'] = '%s:%d' % (conf.host, conf.port)\n    os.environ['APP_NAME'] = appname\n    os.environ['APP_VERSION'] = appversion\n\nclass Worker:\n    def __init__(self, conf, app):\n        self.conf = conf\n        self.application = app\n        self.collect_statifiles()\n\n    def collect_statifiles(self):\n        self.static_files = {}\n        if hasattr(self.conf, 'handlers'):\n            for h in self.conf.handlers:\n                url = h['url']\n                if h.has_key('static_dir'):\n                    self.static_files[url] = os.path.join(app_root, h['static_dir'])\n                elif h.has_key('static_path'):\n                    self.static_files[url] = os.path.join(app_root, h['static_path'])\n        if not len(self.static_files):\n            self.static_files.update({\n                '/static': os.path.join(app_root,  'static'),\n                '/media': os.path.join(app_root,  'media'),\n                '/favicon.ico': os.path.join(app_root,  'favicon.ico'),\n            })\n        import sae\n        self.static_files['/_sae/channel/api.js'] = os.path.join(os.path.dirname(sae.__file__), 'channel.js')\n\n        if self.conf.storage:\n            # stor dispatch: for test usage only\n            self.static_files['/stor-stub/'] = os.path.abspath(self.conf.storage)\n\n    def run(self):\n        raise NotImplementedError()\n\nclass WsgiWorker(Worker):\n    def run(self):\n        # FIXME: All files under current directory\n        files = ['index.wsgi']\n\n        # XXX:\n        # when django template renders `environ` in its 500 page, it will\n        # try to call `environ['werkzeug.server.shutdown'` and cause the\n        # server exit unexpectedly.\n        # See: https://docs.djangoproject.com/en/dev/ref/templates/api/#variables-and-lookups\n        def wrap(app):\n            def _(environ, start_response):\n                try:\n                    del environ['werkzeug.server.shutdown']\n                except KeyError:\n                    pass\n                return app(environ, start_response)\n            return _\n\n        if 'WERKZEUG_RUN_MAIN' in os.environ:\n            os.environ['sae.run_main'] = '1'\n\n        self.application = _channel_wrapper(self.application)\n        from werkzeug.serving import run_simple\n        run_simple(self.conf.host, self.conf.port,\n                   wrap(self.application),\n                   use_reloader = True,\n                   use_debugger = True,\n                   extra_files = files,\n                   static_files = self.static_files)\n\nclass TornadoWorker(Worker):\n    def run(self):\n        import tornado.autoreload\n        tornado.autoreload.watch('index.wsgi')\n\n        import re\n        from tornado.web import URLSpec, StaticFileHandler\n        # The user should not use `tornado.web.Application.add_handlers`\n        # since here in SAE one application only has a single host, so here\n        # we can just use the first host_handers.\n        handlers = self.application.handlers[0][1]\n        for prefix, path in self.static_files.iteritems():\n            pattern = re.escape(prefix) + r\"(.*)\"\n            handlers.insert(0, URLSpec(pattern, StaticFileHandler, {\"path\": path}))\n\n        os.environ['sae.run_main'] = '1'\n\n        import tornado.ioloop\n        from tornado.httpserver import HTTPServer\n        server = HTTPServer(self.application, xheaders=True)\n        server.listen(self.conf.port, self.conf.host)\n        tornado.ioloop.IOLoop.instance().start()\n\ndef main(options):\n    conf_path = os.path.join(app_root, 'config.yaml')\n    conf = yaml.load(open(conf_path, \"r\"))\n    options.__dict__.update(conf)\n    conf = options\n\n    # if env `WERKZEUG_RUN_MAIN` is not defined, then we are in \n    # the reloader process.\n    # if os.environ.get('WERKZEUG_RUN_MAIN', False):\n\n    setup_sae_environ(conf)\n\n    try:\n        index = imp.load_source('index', 'index.wsgi')\n    except IOError:\n        print >>sys.stderr, \"Seems you don't have an index.wsgi\"\n        return\n    if not hasattr(index, 'application'):\n        print >>sys.stderr, \"application not found in index.wsgi\"\n        return\n    if not callable(index.application):\n        print >>sys.stderr, \"application is not a callable\"\n        return\n\n    application = index.application\n\n    cls_name = getattr(conf, 'worker', 'wsgi').capitalize() + 'Worker'\n    try:\n        globals().get(cls_name, WsgiWorker)(conf, application).run()\n    except KeyboardInterrupt:\n        pass\n\nif __name__ == '__main__':\n    parser = OptionParser()\n    parser.add_option(\"-p\", \"--port\", type=\"int\", dest=\"port\", default=\"8080\",\n                      help=\"Which port to listen\")\n    parser.add_option(\"--host\", dest=\"host\", default=\"localhost\",\n                      help=\"Which host to listen\")\n    parser.add_option(\"--mysql\", dest=\"mysql\", help=\"Mysql configuration: user:password@host:port\")\n    parser.add_option(\"--storage-path\", dest=\"storage\", help=\"Directory used as local stoarge\")\n    parser.add_option(\"--kvdb-file\", dest=\"kvdb\", help=\"File to save kvdb data\")\n    (options, args) = parser.parse_args()\n\n    app_root = search_file_bottom_up('config.yaml')\n    if app_root is None:\n        print >> sys.stderr, \\\n            'Error: Not an app directory(or any of the parent directories)'\n        sys.exit(1)\n    if app_root != os.getcwd(): os.chdir(app_root)\n\n    main(options)\n"
  },
  {
    "path": "dev_server/sae/__init__.py",
    "content": "\"\"\"SAE Python\n\nJaime Chen<chenzheng2@staff.sina.com.cn> 2011\n\"\"\"\n\nimport core\n\ndef create_wsgi_app(app):\n    return app\n\ndef dev_server(conf):\n    core.environ = conf\n\nimport sys, os.path\n_PYTHON_VERSION = 'python%d.%d' % (sys.version_info[0], sys.version_info[1])\n\ndef add_vendor_dir(path, index=1):\n    \"\"\"Insert site dir or virtualenv at a given index in sys.path.\n\n    Args:\n      path: relative path to a site dir or virtualenv.\n      index: sys.path position to insert the site dir.\n\n    Raises:\n      ValueError: path doesn't exist.\n    \"\"\"\n    venv_path = os.path.join(path, 'lib', _PYTHON_VERSION, 'site-packages')\n    if os.path.isdir(venv_path):\n        site_dir = venv_path\n    elif os.path.isdir(path):\n        site_dir = path\n    else:\n        raise ValueError('virtualenv: cannot access %s: '\n                         'No such virtualenv or site directory' % path)\n\n    sys_path = sys.path[:]\n    del sys.path[index:]\n    import site\n    site.addsitedir(site_dir)\n    sys.path.extend(sys_path[index:])\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/__init__.py",
    "content": "\"\"\"MySQLdb - A DB API v2.0 compatible interface to MySQL.\n\nThis package is a wrapper around _mysql, which mostly implements the\nMySQL C API.\n\nconnect() -- connects to server\n\nSee the C API specification and the MySQL documentation for more info\non other items.\n\nFor information on how MySQLdb handles type conversion, see the\nMySQLdb.converters module.\n\n\"\"\"\n\n__revision__ = \"\"\"$Revision: 603 $\"\"\"[11:-2]\nfrom release import __version__, version_info, __author__\n\nimport _mysql\n\nif version_info != _mysql.version_info:\n    raise ImportError(\"this is MySQLdb version %s, but _mysql is version %r\" %\n                      (version_info, _mysql.version_info))\n\nthreadsafety = 1\napilevel = \"2.0\"\nparamstyle = \"format\"\n\nfrom _mysql import *\nfrom constants import FIELD_TYPE\nfrom times import Date, Time, Timestamp, \\\n    DateFromTicks, TimeFromTicks, TimestampFromTicks\n\ntry:\n    frozenset\nexcept NameError:\n    from sets import ImmutableSet as frozenset\n\nclass DBAPISet(frozenset):\n\n    \"\"\"A special type of set for which A == x is true if A is a\n    DBAPISet and x is a member of that set.\"\"\"\n\n    def __eq__(self, other):\n        if isinstance(other, DBAPISet):\n            return not self.difference(other)\n        return other in self\n\n\nSTRING    = DBAPISet([FIELD_TYPE.ENUM, FIELD_TYPE.STRING,\n                     FIELD_TYPE.VAR_STRING])\nBINARY    = DBAPISet([FIELD_TYPE.BLOB, FIELD_TYPE.LONG_BLOB,\n                     FIELD_TYPE.MEDIUM_BLOB, FIELD_TYPE.TINY_BLOB])\nNUMBER    = DBAPISet([FIELD_TYPE.DECIMAL, FIELD_TYPE.DOUBLE, FIELD_TYPE.FLOAT,\n                     FIELD_TYPE.INT24, FIELD_TYPE.LONG, FIELD_TYPE.LONGLONG,\n                     FIELD_TYPE.TINY, FIELD_TYPE.YEAR])\nDATE      = DBAPISet([FIELD_TYPE.DATE, FIELD_TYPE.NEWDATE])\nTIME      = DBAPISet([FIELD_TYPE.TIME])\nTIMESTAMP = DBAPISet([FIELD_TYPE.TIMESTAMP, FIELD_TYPE.DATETIME])\nDATETIME  = TIMESTAMP\nROWID     = DBAPISet()\n\ndef test_DBAPISet_set_equality():\n    assert STRING == STRING\n\ndef test_DBAPISet_set_inequality():\n    assert STRING != NUMBER\n\ndef test_DBAPISet_set_equality_membership():\n    assert FIELD_TYPE.VAR_STRING == STRING\n\ndef test_DBAPISet_set_inequality_membership():\n    assert FIELD_TYPE.DATE != STRING\n\ndef Binary(x):\n    return str(x)\n\ndef Connect(*args, **kwargs):\n    \"\"\"Factory function for connections.Connection.\"\"\"\n    from connections import Connection\n    return Connection(*args, **kwargs)\n\nconnect = Connection = Connect\n\n__all__ = [ 'BINARY', 'Binary', 'Connect', 'Connection', 'DATE',\n    'Date', 'Time', 'Timestamp', 'DateFromTicks', 'TimeFromTicks',\n    'TimestampFromTicks', 'DataError', 'DatabaseError', 'Error',\n    'FIELD_TYPE', 'IntegrityError', 'InterfaceError', 'InternalError',\n    'MySQLError', 'NULL', 'NUMBER', 'NotSupportedError', 'DBAPISet',\n    'OperationalError', 'ProgrammingError', 'ROWID', 'STRING', 'TIME',\n    'TIMESTAMP', 'Warning', 'apilevel', 'connect', 'connections',\n    'constants', 'converters', 'cursors', 'debug', 'escape', 'escape_dict',\n    'escape_sequence', 'escape_string', 'get_client_info',\n    'paramstyle', 'string_literal', 'threadsafety', 'version_info']\n\n\n\n\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/_mysql.py",
    "content": "\n# Copyright (C) 2012-2013 SINA, All rights reserved.\n\nfrom _mysql_exceptions import *\n\n\"\"\"An proxy for the MySQL C API\nTranslate the _mysql C API call into restfull call\n\"\"\"\n\nimport types\nimport pickle\nimport urllib2\n\nfrom release import __version__, version_info\n\nimport logging\nlogger = logging.getLogger('sae._mysql')\n\nNULL = 'NULL'\n_SAE_MYSQL_API_BACKEND = 'http://2.python.sinaapp.com/api/mysql/'\n\n# refs:\n# http://www.python.org/dev/peps/pep-0249\n# http://mysql-python.sourceforge.net/MySQLdb.html#mysql\n\nclass connection(object):\n    def __init__(self, *args, **kwargs):\n        self.converter = kwargs.pop('conv', {})\n        self._conn_args = args\n        self._conn_kwargs = kwargs\n        self._conn_id = None\n        self._rows = None\n        self._description = None\n        self._description_flags = None\n        self._rowcount = None\n        self._warnings = None\n        self._info = None\n        self._lastrowid = None\n        self._open_connection()\n\n    def open(self):\n        pass\n\n    def close(self):\n        self._conn_id = None\n\n    def shutdown(self):\n        pass\n\n    def select_db(self, *args):\n        self._request('select_db', args)\n\n    def change_user(self):\n        pass\n\n    def character_set_name(self):\n        if not hasattr(self, '_charset'):\n            self._charset = self._request('character_set_name')\n        return self._charset\n\n    def set_character_set(self, charset):\n        if getattr(self, '_charset', None) != charset:\n            self._request('set_character_set', charset)\n        self._charset = charset\n\n    def set_server_option(self, *args, **kws):\n        logging.warning('Ignored set_server_option: %s, %s', args, kws)\n\n    def query(self, query):\n        retval = self._request('query', query=query)\n        self._rows = retval['rows']\n        self._description = retval['description']\n        self._description_flags = retval['description_flags']\n        self._rowcount = retval['rowcount']\n        self._warnings = retval['warnings']\n        self._info = retval['info']\n        self._lastrowid = retval['lastrowid']\n\n    def commit(self):\n        pass\n\n    def rollback(self):\n        pass\n\n    def autocommit(self, value):\n        pass\n\n    def use_result(self):\n        return self.store_result()\n\n    def store_result(self):\n        if self._rows:\n            return StoreResult(self, self._rows, self.converter,\n                               self._description, self._description_flags)\n        else:\n            return None\n\n    def next_result(self, *args, **kws):\n        # TODO\n        return -1\n\n    def affected_rows(self):\n        return self._rowcount\n\n    def insert_id(self):\n        return self._lastrowid\n\n    def info(self):\n        return self._info\n\n    def get_host_info(self):\n        return self._host_info\n\n    def get_proto_info(self):\n        return self._proto_info\n\n    def get_server_info(self):\n        return self._server_info\n\n    def ping(self):\n        pass\n\n    def escape(self, item, dct=None):\n        return escape(item, dct or self.converter)\n        \n    def escape_string(self, str):\n        return escape_string(str)\n\n    def string_literal(self, obj):\n        return '\\'%s\\'' % escape_string(str(obj)) \n\n    def _open_connection(self):\n        retval = self._request('open', *self._conn_args, **self._conn_kwargs)\n        self._conn_id = retval['connection_id']\n        self._host_info = retval['host_info']\n        self._proto_info = retval['proto_info']\n        self._server_info = retval['server_info']\n        self.server_capabilities = retval['server_capabilities']\n\n    def _request(self, op, *args, **kwargs):\n        req = {\n            'connection_id': self._conn_id,\n            'op': op, 'args': args, 'kwargs': kwargs\n        }\n        logger.debug('REQ: %s', req)\n        payload = pickle.dumps(req)\n        body = urllib2.urlopen(_SAE_MYSQL_API_BACKEND, payload).read()\n        rep = pickle.loads(body)\n        logger.debug('REP: %s', rep)\n        if rep.get('sql_exception'):\n            raise rep.get('sql_exception')\n        return rep.get('result')\n\ndef _mysql_rows_to_python(rows, conv, field_info, how):\n    def row_to_python0(row):\n        nrow = []\n        for i, v in enumerate(row):\n            # XXX: NULL is always converted to None, so here we\n            # do not need to do it again.\n            nrow.append(None if v is None else conv[i](v))\n        return tuple(nrow)\n    def row_to_python1(row):\n        nrow = {}\n        for i, v in enumerate(row):\n            # XXX: NULL is always converted to None, so here we\n            # do not need to do it again.\n            nrow[field_info[i][0]] = None if v is None else conv[i](v)\n        return nrow\n    if how:\n        return tuple(row_to_python1(r) for r in rows)\n    else:\n        return tuple(row_to_python0(r) for r in rows)\n\nclass StoreResult:\n    def __init__(self, conn, rows, conv, description, description_flags):\n        self.conn = conn\n        self.current = 0\n        self.description = description\n        self.description_flags = description_flags\n        self._cached = rows\n        self._init_conv(conv)\n\n    def _init_conv(self, conv):\n        # _mysql.c:_mysql_ResultObject_Initialize\n        self.converter = []\n        for n, i in enumerate(self.description):\n            c = conv.get(i[1], str)     # search by field.type\n            if isinstance(c, list):\n                nc = None\n                mask = self.description_flags[n]\n                for j in c:\n                    if isinstance(j[0], int):\n                        if mask & j[0]: # search by field.flags\n                            nc = j[1]\n                            break\n                    else:\n                        nc = j[1]\n                        break           # wildcard\n                c = nc if nc is not None else str\n            self.converter.append(c)\n\n    def fetch_row(self, maxrows=1, how=0):\n        if maxrows == 0:\n            retval = self._cached\n            self._cached = ()\n        else:\n            retval = self._cached[self.current:maxrows]\n            self.current += maxrows\n        return _mysql_rows_to_python(retval, self.converter, self.description, how)\n\n    def describe(self):\n        return self.description\n\n    def field_flags(self):\n        return self.description_flags\n\nconnect = connection\n\ndef get_client_info():\n    return '5.1.67'\n\n\ndef escape(item, dct):\n    return _escape_item(item, dct)\n    \ndef _escape_item(val, dct):\n    d = dct.get(type(val)) or dct.get(types.StringType)\n    return d(val, dct)\n\n# Copied from pymysql\n# See: https://github.com/petehunt/PyMySQL/blob/master/pymysql/converters.py\nimport re\nESCAPE_REGEX = re.compile(r\"[\\0\\n\\r\\032\\'\\\"\\\\]\", re.IGNORECASE)\ndef escape_string(value):\n    def rep(m):\n        n = m.group(0)\n        if n == \"\\0\":\n            return \"\\\\0\"\n        elif n == \"\\n\":\n            return \"\\\\n\"\n        elif n == \"\\r\":\n            return \"\\\\r\"\n        elif n == \"\\032\":\n            return \"\\\\Z\"\n        else:\n            return \"\\\\\"+n\n    s = re.sub(ESCAPE_REGEX, rep, value)\n    return s\n\ndef string_literal(obj):\n    return '\\'%s\\'' % escape_string(str(obj)) \n\ndef escape_dict(val, dct):\n    n = {}\n    for k, v in val.items():\n        quoted = _escape_item(v)\n        n[k] = quoted\n    return n\n\ndef escape_sequence(val, dct):\n    return tuple(_escape_item(v, dct) for v in val)\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/_mysql_exceptions.py",
    "content": "\"\"\"_mysql_exceptions: Exception classes for _mysql and MySQLdb.\n\nThese classes are dictated by the DB API v2.0:\n\n    http://www.python.org/topics/database/DatabaseAPI-2.0.html\n\"\"\"\n\nfrom exceptions import Exception, StandardError, Warning\n\nclass MySQLError(StandardError):\n    \n    \"\"\"Exception related to operation with MySQL.\"\"\"\n\n\nclass Warning(Warning, MySQLError):\n\n    \"\"\"Exception raised for important warnings like data truncations\n    while inserting, etc.\"\"\"\n\nclass Error(MySQLError):\n\n    \"\"\"Exception that is the base class of all other error exceptions\n    (not Warning).\"\"\"\n\n\nclass InterfaceError(Error):\n\n    \"\"\"Exception raised for errors that are related to the database\n    interface rather than the database itself.\"\"\"\n\n\nclass DatabaseError(Error):\n\n    \"\"\"Exception raised for errors that are related to the\n    database.\"\"\"\n\n\nclass DataError(DatabaseError):\n\n    \"\"\"Exception raised for errors that are due to problems with the\n    processed data like division by zero, numeric value out of range,\n    etc.\"\"\"\n\n\nclass OperationalError(DatabaseError):\n\n    \"\"\"Exception raised for errors that are related to the database's\n    operation and not necessarily under the control of the programmer,\n    e.g. an unexpected disconnect occurs, the data source name is not\n    found, a transaction could not be processed, a memory allocation\n    error occurred during processing, etc.\"\"\"\n\n\nclass IntegrityError(DatabaseError):\n\n    \"\"\"Exception raised when the relational integrity of the database\n    is affected, e.g. a foreign key check fails, duplicate key,\n    etc.\"\"\"\n\n\nclass InternalError(DatabaseError):\n\n    \"\"\"Exception raised when the database encounters an internal\n    error, e.g. the cursor is not valid anymore, the transaction is\n    out of sync, etc.\"\"\"\n\n\nclass ProgrammingError(DatabaseError):\n\n    \"\"\"Exception raised for programming errors, e.g. table not found\n    or already exists, syntax error in the SQL statement, wrong number\n    of parameters specified, etc.\"\"\"\n\n\nclass NotSupportedError(DatabaseError):\n\n    \"\"\"Exception raised in case a method or database API was used\n    which is not supported by the database, e.g. requesting a\n    .rollback() on a connection that does not support transaction or\n    has transactions turned off.\"\"\"\n\n\ndel Exception, StandardError\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/connections.py",
    "content": "\"\"\"\n\nThis module implements connections for MySQLdb. Presently there is\nonly one class: Connection. Others are unlikely. However, you might\nwant to make your own subclasses. In most cases, you will probably\noverride Connection.default_cursor with a non-standard Cursor class.\n\n\"\"\"\nimport cursors\nfrom _mysql_exceptions import Warning, Error, InterfaceError, DataError, \\\n     DatabaseError, OperationalError, IntegrityError, InternalError, \\\n     NotSupportedError, ProgrammingError\nimport types, _mysql\nimport re\n\n\ndef defaulterrorhandler(connection, cursor, errorclass, errorvalue):\n    \"\"\"\n\n    If cursor is not None, (errorclass, errorvalue) is appended to\n    cursor.messages; otherwise it is appended to\n    connection.messages. Then errorclass is raised with errorvalue as\n    the value.\n\n    You can override this with your own error handler by assigning it\n    to the instance.\n\n    \"\"\"\n    error = errorclass, errorvalue\n    if cursor:\n        cursor.messages.append(error)\n    else:\n        connection.messages.append(error)\n    del cursor\n    del connection\n    raise errorclass, errorvalue\n\nre_numeric_part = re.compile(r\"^(\\d+)\")\n\ndef numeric_part(s):\n    \"\"\"Returns the leading numeric part of a string.\n    \n    >>> numeric_part(\"20-alpha\")\n    20\n    >>> numeric_part(\"foo\")\n    >>> numeric_part(\"16b\")\n    16\n    \"\"\"\n    \n    m = re_numeric_part.match(s)\n    if m:\n        return int(m.group(1))\n    return None\n\n\nclass Connection(_mysql.connection):\n\n    \"\"\"MySQL Database Connection Object\"\"\"\n\n    default_cursor = cursors.Cursor\n    \n    def __init__(self, *args, **kwargs):\n        \"\"\"\n\n        Create a connection to the database. It is strongly recommended\n        that you only use keyword parameters. Consult the MySQL C API\n        documentation for more information.\n\n        host\n          string, host to connect\n          \n        user\n          string, user to connect as\n\n        passwd\n          string, password to use\n\n        db\n          string, database to use\n\n        port\n          integer, TCP/IP port to connect to\n\n        unix_socket\n          string, location of unix_socket to use\n\n        conv\n          conversion dictionary, see MySQLdb.converters\n\n        connect_timeout\n          number of seconds to wait before the connection attempt\n          fails.\n\n        compress\n          if set, compression is enabled\n\n        named_pipe\n          if set, a named pipe is used to connect (Windows only)\n\n        init_command\n          command which is run once the connection is created\n\n        read_default_file\n          file from which default client values are read\n\n        read_default_group\n          configuration group to use from the default file\n\n        cursorclass\n          class object, used to create cursors (keyword only)\n\n        use_unicode\n          If True, text-like columns are returned as unicode objects\n          using the connection's character set.  Otherwise, text-like\n          columns are returned as strings.  columns are returned as\n          normal strings. Unicode objects will always be encoded to\n          the connection's character set regardless of this setting.\n\n        charset\n          If supplied, the connection character set will be changed\n          to this character set (MySQL-4.1 and newer). This implies\n          use_unicode=True.\n\n        sql_mode\n          If supplied, the session SQL mode will be changed to this\n          setting (MySQL-4.1 and newer). For more details and legal\n          values, see the MySQL documentation.\n          \n        client_flag\n          integer, flags to use or 0\n          (see MySQL docs or constants/CLIENTS.py)\n\n        ssl\n          dictionary or mapping, contains SSL connection parameters;\n          see the MySQL documentation for more details\n          (mysql_ssl_set()).  If this is set, and the client does not\n          support SSL, NotSupportedError will be raised.\n\n        local_infile\n          integer, non-zero enables LOAD LOCAL INFILE; zero disables\n    \n        There are a number of undocumented, non-standard methods. See the\n        documentation for the MySQL C API for some hints on what they do.\n\n        \"\"\"\n        from constants import CLIENT, FIELD_TYPE\n        from converters import conversions\n        from weakref import proxy, WeakValueDictionary\n        \n        import types\n\n        kwargs2 = kwargs.copy()\n        \n        if kwargs.has_key('conv'):\n            conv = kwargs['conv']\n        else:\n            conv = conversions\n\n        conv2 = {}\n        for k, v in conv.items():\n            if isinstance(k, int) and isinstance(v, list):\n                conv2[k] = v[:]\n            else:\n                conv2[k] = v\n        kwargs2['conv'] = conv2\n\n        self.cursorclass = kwargs2.pop('cursorclass', self.default_cursor)\n        charset = kwargs2.pop('charset', '')\n\n        if charset:\n            use_unicode = True\n        else:\n            use_unicode = False\n            \n        use_unicode = kwargs2.pop('use_unicode', use_unicode)\n        sql_mode = kwargs2.pop('sql_mode', '')\n\n        client_flag = kwargs.get('client_flag', 0)\n        client_version = tuple([ numeric_part(n) for n in _mysql.get_client_info().split('.')[:2] ])\n        if client_version >= (4, 1):\n            client_flag |= CLIENT.MULTI_STATEMENTS\n        if client_version >= (5, 0):\n            client_flag |= CLIENT.MULTI_RESULTS\n            \n        kwargs2['client_flag'] = client_flag\n\n        super(Connection, self).__init__(*args, **kwargs2)\n\n        self.encoders = dict([ (k, v) for k, v in conv.items()\n                               if type(k) is not int ])\n        \n        self._server_version = tuple([ numeric_part(n) for n in self.get_server_info().split('.')[:2] ])\n\n        db = proxy(self)\n        def _get_string_literal():\n            def string_literal(obj, dummy=None):\n                return db.string_literal(obj)\n            return string_literal\n\n        def _get_unicode_literal():\n            def unicode_literal(u, dummy=None):\n                return db.literal(u.encode(unicode_literal.charset))\n            return unicode_literal\n\n        def _get_string_decoder():\n            def string_decoder(s):\n                return s.decode(string_decoder.charset)\n            return string_decoder\n        \n        string_literal = _get_string_literal()\n        self.unicode_literal = unicode_literal = _get_unicode_literal()\n        self.string_decoder = string_decoder = _get_string_decoder()\n        if not charset:\n            charset = self.character_set_name()\n        self.set_character_set(charset)\n\n        if sql_mode:\n            self.set_sql_mode(sql_mode)\n\n        if use_unicode:\n            self.converter[FIELD_TYPE.STRING].append((None, string_decoder))\n            self.converter[FIELD_TYPE.VAR_STRING].append((None, string_decoder))\n            self.converter[FIELD_TYPE.VARCHAR].append((None, string_decoder))\n            self.converter[FIELD_TYPE.BLOB].append((None, string_decoder))\n\n        self.encoders[types.StringType] = string_literal\n        self.encoders[types.UnicodeType] = unicode_literal\n        self._transactional = self.server_capabilities & CLIENT.TRANSACTIONS\n        if self._transactional:\n            # PEP-249 requires autocommit to be initially off\n            self.autocommit(False)\n        self.messages = []\n        \n    def cursor(self, cursorclass=None):\n        \"\"\"\n\n        Create a cursor on which queries may be performed. The\n        optional cursorclass parameter is used to create the\n        Cursor. By default, self.cursorclass=cursors.Cursor is\n        used.\n\n        \"\"\"\n        return (cursorclass or self.cursorclass)(self)\n\n    def __enter__(self): return self.cursor()\n    \n    def __exit__(self, exc, value, tb):\n        if exc:\n            self.rollback()\n        else:\n            self.commit()\n            \n    def literal(self, o):\n        \"\"\"\n\n        If o is a single object, returns an SQL literal as a string.\n        If o is a non-string sequence, the items of the sequence are\n        converted and returned as a sequence.\n\n        Non-standard. For internal use; do not use this in your\n        applications.\n\n        \"\"\"\n        return self.escape(o, self.encoders)\n\n    def begin(self):\n        \"\"\"Explicitly begin a connection. Non-standard.\n        DEPRECATED: Will be removed in 1.3.\n        Use an SQL BEGIN statement instead.\"\"\"\n        from warnings import warn\n        warn(\"begin() is non-standard and will be removed in 1.3\",\n             DeprecationWarning, 2)\n        self.query(\"BEGIN\")\n        \n    if not hasattr(_mysql.connection, 'warning_count'):\n\n        def warning_count(self):\n            \"\"\"Return the number of warnings generated from the\n            last query. This is derived from the info() method.\"\"\"\n            from string import atoi\n            info = self.info()\n            if info:\n                return atoi(info.split()[-1])\n            else:\n                return 0\n\n    def set_character_set(self, charset):\n        \"\"\"Set the connection character set to charset. The character\n        set can only be changed in MySQL-4.1 and newer. If you try\n        to change the character set from the current value in an\n        older version, NotSupportedError will be raised.\"\"\"\n        if self.character_set_name() != charset:\n            try:\n                super(Connection, self).set_character_set(charset)\n            except AttributeError:\n                if self._server_version < (4, 1):\n                    raise NotSupportedError(\"server is too old to set charset\")\n                self.query('SET NAMES %s' % charset)\n                self.store_result()\n        self.string_decoder.charset = charset\n        self.unicode_literal.charset = charset\n\n    def set_sql_mode(self, sql_mode):\n        \"\"\"Set the connection sql_mode. See MySQL documentation for\n        legal values.\"\"\"\n        if self._server_version < (4, 1):\n            raise NotSupportedError(\"server is too old to set sql_mode\")\n        self.query(\"SET SESSION sql_mode='%s'\" % sql_mode)\n        self.store_result()\n        \n    def show_warnings(self):\n        \"\"\"Return detailed information about warnings as a\n        sequence of tuples of (Level, Code, Message). This\n        is only supported in MySQL-4.1 and up. If your server\n        is an earlier version, an empty sequence is returned.\"\"\"\n        if self._server_version < (4,1): return ()\n        self.query(\"SHOW WARNINGS\")\n        r = self.store_result()\n        warnings = r.fetch_row(0)\n        return warnings\n    \n    Warning = Warning\n    Error = Error\n    InterfaceError = InterfaceError\n    DatabaseError = DatabaseError\n    DataError = DataError\n    OperationalError = OperationalError\n    IntegrityError = IntegrityError\n    InternalError = InternalError\n    ProgrammingError = ProgrammingError\n    NotSupportedError = NotSupportedError\n\n    errorhandler = defaulterrorhandler\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/constants/CLIENT.py",
    "content": "\"\"\"MySQL CLIENT constants\n\nThese constants are used when creating the connection. Use bitwise-OR\n(|) to combine options together, and pass them as the client_flags\nparameter to MySQLdb.Connection. For more information on these flags,\nsee the MySQL C API documentation for mysql_real_connect().\n\n\"\"\"\n\nLONG_PASSWORD = 1\nFOUND_ROWS = 2\nLONG_FLAG = 4\nCONNECT_WITH_DB = 8\nNO_SCHEMA = 16\nCOMPRESS = 32\nODBC = 64\nLOCAL_FILES = 128\nIGNORE_SPACE = 256\nCHANGE_USER = 512\nINTERACTIVE = 1024\nSSL = 2048\nIGNORE_SIGPIPE = 4096\nTRANSACTIONS = 8192 # mysql_com.h was WRONG prior to 3.23.35\nRESERVED = 16384\nSECURE_CONNECTION = 32768\nMULTI_STATEMENTS = 65536\nMULTI_RESULTS = 131072\n\n\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/constants/CR.py",
    "content": "\"\"\"MySQL Connection Errors\n\nNearly all of these raise OperationalError. COMMANDS_OUT_OF_SYNC\nraises ProgrammingError.\n\n\"\"\"\n\nMIN_ERROR = 2000\nMAX_ERROR = 2999\nUNKNOWN_ERROR = 2000\nSOCKET_CREATE_ERROR = 2001\nCONNECTION_ERROR = 2002\nCONN_HOST_ERROR = 2003\nIPSOCK_ERROR = 2004\nUNKNOWN_HOST = 2005\nSERVER_GONE_ERROR = 2006\nVERSION_ERROR = 2007\nOUT_OF_MEMORY = 2008\nWRONG_HOST_INFO = 2009\nLOCALHOST_CONNECTION = 2010\nTCP_CONNECTION = 2011\nSERVER_HANDSHAKE_ERR = 2012\nSERVER_LOST = 2013\nCOMMANDS_OUT_OF_SYNC = 2014\nNAMEDPIPE_CONNECTION = 2015\nNAMEDPIPEWAIT_ERROR = 2016\nNAMEDPIPEOPEN_ERROR = 2017\nNAMEDPIPESETSTATE_ERROR = 2018\nCANT_READ_CHARSET = 2019\nNET_PACKET_TOO_LARGE = 2020\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/constants/ER.py",
    "content": "\"\"\"MySQL ER Constants\n\nThese constants are error codes for the bulk of the error conditions\nthat may occur.\n\n\"\"\"\n\nHASHCHK = 1000\nNISAMCHK = 1001\nNO = 1002\nYES = 1003\nCANT_CREATE_FILE = 1004\nCANT_CREATE_TABLE = 1005\nCANT_CREATE_DB = 1006\nDB_CREATE_EXISTS = 1007\nDB_DROP_EXISTS = 1008\nDB_DROP_DELETE = 1009\nDB_DROP_RMDIR = 1010\nCANT_DELETE_FILE = 1011\nCANT_FIND_SYSTEM_REC = 1012\nCANT_GET_STAT = 1013\nCANT_GET_WD = 1014\nCANT_LOCK = 1015\nCANT_OPEN_FILE = 1016\nFILE_NOT_FOUND = 1017\nCANT_READ_DIR = 1018\nCANT_SET_WD = 1019\nCHECKREAD = 1020\nDISK_FULL = 1021\nDUP_KEY = 1022\nERROR_ON_CLOSE = 1023\nERROR_ON_READ = 1024\nERROR_ON_RENAME = 1025\nERROR_ON_WRITE = 1026\nFILE_USED = 1027\nFILSORT_ABORT = 1028\nFORM_NOT_FOUND = 1029\nGET_ERRNO = 1030\nILLEGAL_HA = 1031\nKEY_NOT_FOUND = 1032\nNOT_FORM_FILE = 1033\nNOT_KEYFILE = 1034\nOLD_KEYFILE = 1035\nOPEN_AS_READONLY = 1036\nOUTOFMEMORY = 1037\nOUT_OF_SORTMEMORY = 1038\nUNEXPECTED_EOF = 1039\nCON_COUNT_ERROR = 1040\nOUT_OF_RESOURCES = 1041\nBAD_HOST_ERROR = 1042\nHANDSHAKE_ERROR = 1043\nDBACCESS_DENIED_ERROR = 1044\nACCESS_DENIED_ERROR = 1045\nNO_DB_ERROR = 1046\nUNKNOWN_COM_ERROR = 1047\nBAD_NULL_ERROR = 1048\nBAD_DB_ERROR = 1049\nTABLE_EXISTS_ERROR = 1050\nBAD_TABLE_ERROR = 1051\nNON_UNIQ_ERROR = 1052\nSERVER_SHUTDOWN = 1053\nBAD_FIELD_ERROR = 1054\nWRONG_FIELD_WITH_GROUP = 1055\nWRONG_GROUP_FIELD = 1056\nWRONG_SUM_SELECT = 1057\nWRONG_VALUE_COUNT = 1058\nTOO_LONG_IDENT = 1059\nDUP_FIELDNAME = 1060\nDUP_KEYNAME = 1061\nDUP_ENTRY = 1062\nWRONG_FIELD_SPEC = 1063\nPARSE_ERROR = 1064\nEMPTY_QUERY = 1065\nNONUNIQ_TABLE = 1066\nINVALID_DEFAULT = 1067\nMULTIPLE_PRI_KEY = 1068\nTOO_MANY_KEYS = 1069\nTOO_MANY_KEY_PARTS = 1070\nTOO_LONG_KEY = 1071\nKEY_COLUMN_DOES_NOT_EXITS = 1072\nBLOB_USED_AS_KEY = 1073\nTOO_BIG_FIELDLENGTH = 1074\nWRONG_AUTO_KEY = 1075\nREADY = 1076\nNORMAL_SHUTDOWN = 1077\nGOT_SIGNAL = 1078\nSHUTDOWN_COMPLETE = 1079\nFORCING_CLOSE = 1080\nIPSOCK_ERROR = 1081\nNO_SUCH_INDEX = 1082\nWRONG_FIELD_TERMINATORS = 1083\nBLOBS_AND_NO_TERMINATED = 1084\nTEXTFILE_NOT_READABLE = 1085\nFILE_EXISTS_ERROR = 1086\nLOAD_INFO = 1087\nALTER_INFO = 1088\nWRONG_SUB_KEY = 1089\nCANT_REMOVE_ALL_FIELDS = 1090\nCANT_DROP_FIELD_OR_KEY = 1091\nINSERT_INFO = 1092\nINSERT_TABLE_USED = 1093\nNO_SUCH_THREAD = 1094\nKILL_DENIED_ERROR = 1095\nNO_TABLES_USED = 1096\nTOO_BIG_SET = 1097\nNO_UNIQUE_LOGFILE = 1098\nTABLE_NOT_LOCKED_FOR_WRITE = 1099\nTABLE_NOT_LOCKED = 1100\nBLOB_CANT_HAVE_DEFAULT = 1101\nWRONG_DB_NAME = 1102\nWRONG_TABLE_NAME = 1103\nTOO_BIG_SELECT = 1104\nUNKNOWN_ERROR = 1105\nUNKNOWN_PROCEDURE = 1106\nWRONG_PARAMCOUNT_TO_PROCEDURE = 1107\nWRONG_PARAMETERS_TO_PROCEDURE = 1108\nUNKNOWN_TABLE = 1109\nFIELD_SPECIFIED_TWICE = 1110\nINVALID_GROUP_FUNC_USE = 1111\nUNSUPPORTED_EXTENSION = 1112\nTABLE_MUST_HAVE_COLUMNS = 1113\nRECORD_FILE_FULL = 1114\nUNKNOWN_CHARACTER_SET = 1115\nTOO_MANY_TABLES = 1116\nTOO_MANY_FIELDS = 1117\nTOO_BIG_ROWSIZE = 1118\nSTACK_OVERRUN = 1119\nWRONG_OUTER_JOIN = 1120\nNULL_COLUMN_IN_INDEX = 1121\nCANT_FIND_UDF = 1122\nCANT_INITIALIZE_UDF = 1123\nUDF_NO_PATHS = 1124\nUDF_EXISTS = 1125\nCANT_OPEN_LIBRARY = 1126\nCANT_FIND_DL_ENTRY = 1127\nFUNCTION_NOT_DEFINED = 1128\nHOST_IS_BLOCKED = 1129\nHOST_NOT_PRIVILEGED = 1130\nPASSWORD_ANONYMOUS_USER = 1131\nPASSWORD_NOT_ALLOWED = 1132\nPASSWORD_NO_MATCH = 1133\nUPDATE_INFO = 1134\nCANT_CREATE_THREAD = 1135\nWRONG_VALUE_COUNT_ON_ROW = 1136\nCANT_REOPEN_TABLE = 1137\nINVALID_USE_OF_NULL = 1138\nREGEXP_ERROR = 1139\nMIX_OF_GROUP_FUNC_AND_FIELDS = 1140\nNONEXISTING_GRANT = 1141\nTABLEACCESS_DENIED_ERROR = 1142\nCOLUMNACCESS_DENIED_ERROR = 1143\nILLEGAL_GRANT_FOR_TABLE = 1144\nGRANT_WRONG_HOST_OR_USER = 1145\nNO_SUCH_TABLE = 1146\nNONEXISTING_TABLE_GRANT = 1147\nNOT_ALLOWED_COMMAND = 1148\nSYNTAX_ERROR = 1149\nDELAYED_CANT_CHANGE_LOCK = 1150\nTOO_MANY_DELAYED_THREADS = 1151\nABORTING_CONNECTION = 1152\nNET_PACKET_TOO_LARGE = 1153\nNET_READ_ERROR_FROM_PIPE = 1154\nNET_FCNTL_ERROR = 1155\nNET_PACKETS_OUT_OF_ORDER = 1156\nNET_UNCOMPRESS_ERROR = 1157\nNET_READ_ERROR = 1158\nNET_READ_INTERRUPTED = 1159\nNET_ERROR_ON_WRITE = 1160\nNET_WRITE_INTERRUPTED = 1161\nTOO_LONG_STRING = 1162\nTABLE_CANT_HANDLE_BLOB = 1163\nTABLE_CANT_HANDLE_AUTO_INCREMENT = 1164\nDELAYED_INSERT_TABLE_LOCKED = 1165\nWRONG_COLUMN_NAME = 1166\nWRONG_KEY_COLUMN = 1167\nWRONG_MRG_TABLE = 1168\nDUP_UNIQUE = 1169\nBLOB_KEY_WITHOUT_LENGTH = 1170\nPRIMARY_CANT_HAVE_NULL = 1171\nTOO_MANY_ROWS = 1172\nREQUIRES_PRIMARY_KEY = 1173\nNO_RAID_COMPILED = 1174\nUPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175\nKEY_DOES_NOT_EXITS = 1176\nCHECK_NO_SUCH_TABLE = 1177\nCHECK_NOT_IMPLEMENTED = 1178\nCANT_DO_THIS_DURING_AN_TRANSACTION = 1179\nERROR_DURING_COMMIT = 1180\nERROR_DURING_ROLLBACK = 1181\nERROR_DURING_FLUSH_LOGS = 1182\nERROR_DURING_CHECKPOINT = 1183\nNEW_ABORTING_CONNECTION = 1184\nDUMP_NOT_IMPLEMENTED = 1185\nFLUSH_MASTER_BINLOG_CLOSED = 1186\nINDEX_REBUILD = 1187\nMASTER = 1188\nMASTER_NET_READ = 1189\nMASTER_NET_WRITE = 1190\nFT_MATCHING_KEY_NOT_FOUND = 1191\nLOCK_OR_ACTIVE_TRANSACTION = 1192\nUNKNOWN_SYSTEM_VARIABLE = 1193\nCRASHED_ON_USAGE = 1194\nCRASHED_ON_REPAIR = 1195\nWARNING_NOT_COMPLETE_ROLLBACK = 1196\nTRANS_CACHE_FULL = 1197\nSLAVE_MUST_STOP = 1198\nSLAVE_NOT_RUNNING = 1199\nBAD_SLAVE = 1200\nMASTER_INFO = 1201\nSLAVE_THREAD = 1202\nTOO_MANY_USER_CONNECTIONS = 1203\nSET_CONSTANTS_ONLY = 1204\nLOCK_WAIT_TIMEOUT = 1205\nLOCK_TABLE_FULL = 1206\nREAD_ONLY_TRANSACTION = 1207\nDROP_DB_WITH_READ_LOCK = 1208\nCREATE_DB_WITH_READ_LOCK = 1209\nWRONG_ARGUMENTS = 1210\nNO_PERMISSION_TO_CREATE_USER = 1211\nUNION_TABLES_IN_DIFFERENT_DIR = 1212\nLOCK_DEADLOCK = 1213\nTABLE_CANT_HANDLE_FT = 1214\nCANNOT_ADD_FOREIGN = 1215\nNO_REFERENCED_ROW = 1216\nROW_IS_REFERENCED = 1217\nCONNECT_TO_MASTER = 1218\nQUERY_ON_MASTER = 1219\nERROR_WHEN_EXECUTING_COMMAND = 1220\nWRONG_USAGE = 1221\nWRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222\nCANT_UPDATE_WITH_READLOCK = 1223\nMIXING_NOT_ALLOWED = 1224\nDUP_ARGUMENT = 1225\nUSER_LIMIT_REACHED = 1226\nSPECIFIC_ACCESS_DENIED_ERROR = 1227\nLOCAL_VARIABLE = 1228\nGLOBAL_VARIABLE = 1229\nNO_DEFAULT = 1230\nWRONG_VALUE_FOR_VAR = 1231\nWRONG_TYPE_FOR_VAR = 1232\nVAR_CANT_BE_READ = 1233\nCANT_USE_OPTION_HERE = 1234\nNOT_SUPPORTED_YET = 1235\nMASTER_FATAL_ERROR_READING_BINLOG = 1236\nSLAVE_IGNORED_TABLE = 1237\nINCORRECT_GLOBAL_LOCAL_VAR = 1238\nWRONG_FK_DEF = 1239\nKEY_REF_DO_NOT_MATCH_TABLE_REF = 1240\nOPERAND_COLUMNS = 1241\nSUBQUERY_NO_1_ROW = 1242\nUNKNOWN_STMT_HANDLER = 1243\nCORRUPT_HELP_DB = 1244\nCYCLIC_REFERENCE = 1245\nAUTO_CONVERT = 1246\nILLEGAL_REFERENCE = 1247\nDERIVED_MUST_HAVE_ALIAS = 1248\nSELECT_REDUCED = 1249\nTABLENAME_NOT_ALLOWED_HERE = 1250\nNOT_SUPPORTED_AUTH_MODE = 1251\nSPATIAL_CANT_HAVE_NULL = 1252\nCOLLATION_CHARSET_MISMATCH = 1253\nSLAVE_WAS_RUNNING = 1254\nSLAVE_WAS_NOT_RUNNING = 1255\nTOO_BIG_FOR_UNCOMPRESS = 1256\nZLIB_Z_MEM_ERROR = 1257\nZLIB_Z_BUF_ERROR = 1258\nZLIB_Z_DATA_ERROR = 1259\nCUT_VALUE_GROUP_CONCAT = 1260\nWARN_TOO_FEW_RECORDS = 1261\nWARN_TOO_MANY_RECORDS = 1262\nWARN_NULL_TO_NOTNULL = 1263\nWARN_DATA_OUT_OF_RANGE = 1264\nWARN_DATA_TRUNCATED = 1265\nWARN_USING_OTHER_HANDLER = 1266\nCANT_AGGREGATE_2COLLATIONS = 1267\nDROP_USER = 1268\nREVOKE_GRANTS = 1269\nCANT_AGGREGATE_3COLLATIONS = 1270\nCANT_AGGREGATE_NCOLLATIONS = 1271\nVARIABLE_IS_NOT_STRUCT = 1272\nUNKNOWN_COLLATION = 1273\nSLAVE_IGNORED_SSL_PARAMS = 1274\nSERVER_IS_IN_SECURE_AUTH_MODE = 1275\nWARN_FIELD_RESOLVED = 1276\nBAD_SLAVE_UNTIL_COND = 1277\nMISSING_SKIP_SLAVE = 1278\nUNTIL_COND_IGNORED = 1279\nWRONG_NAME_FOR_INDEX = 1280\nWRONG_NAME_FOR_CATALOG = 1281\nWARN_QC_RESIZE = 1282\nBAD_FT_COLUMN = 1283\nUNKNOWN_KEY_CACHE = 1284\nWARN_HOSTNAME_WONT_WORK = 1285\nUNKNOWN_STORAGE_ENGINE = 1286\nWARN_DEPRECATED_SYNTAX = 1287\nNON_UPDATABLE_TABLE = 1288\nFEATURE_DISABLED = 1289\nOPTION_PREVENTS_STATEMENT = 1290\nDUPLICATED_VALUE_IN_TYPE = 1291\nTRUNCATED_WRONG_VALUE = 1292\nTOO_MUCH_AUTO_TIMESTAMP_COLS = 1293\nINVALID_ON_UPDATE = 1294\nUNSUPPORTED_PS = 1295\nGET_ERRMSG = 1296\nGET_TEMPORARY_ERRMSG = 1297\nUNKNOWN_TIME_ZONE = 1298\nWARN_INVALID_TIMESTAMP = 1299\nINVALID_CHARACTER_STRING = 1300\nWARN_ALLOWED_PACKET_OVERFLOWED = 1301\nCONFLICTING_DECLARATIONS = 1302\nSP_NO_RECURSIVE_CREATE = 1303\nSP_ALREADY_EXISTS = 1304\nSP_DOES_NOT_EXIST = 1305\nSP_DROP_FAILED = 1306\nSP_STORE_FAILED = 1307\nSP_LILABEL_MISMATCH = 1308\nSP_LABEL_REDEFINE = 1309\nSP_LABEL_MISMATCH = 1310\nSP_UNINIT_VAR = 1311\nSP_BADSELECT = 1312\nSP_BADRETURN = 1313\nSP_BADSTATEMENT = 1314\nUPDATE_LOG_DEPRECATED_IGNORED = 1315\nUPDATE_LOG_DEPRECATED_TRANSLATED = 1316\nQUERY_INTERRUPTED = 1317\nSP_WRONG_NO_OF_ARGS = 1318\nSP_COND_MISMATCH = 1319\nSP_NORETURN = 1320\nSP_NORETURNEND = 1321\nSP_BAD_CURSOR_QUERY = 1322\nSP_BAD_CURSOR_SELECT = 1323\nSP_CURSOR_MISMATCH = 1324\nSP_CURSOR_ALREADY_OPEN = 1325\nSP_CURSOR_NOT_OPEN = 1326\nSP_UNDECLARED_VAR = 1327\nSP_WRONG_NO_OF_FETCH_ARGS = 1328\nSP_FETCH_NO_DATA = 1329\nSP_DUP_PARAM = 1330\nSP_DUP_VAR = 1331\nSP_DUP_COND = 1332\nSP_DUP_CURS = 1333\nSP_CANT_ALTER = 1334\nSP_SUBSELECT_NYI = 1335\nSTMT_NOT_ALLOWED_IN_SF_OR_TRG = 1336\nSP_VARCOND_AFTER_CURSHNDLR = 1337\nSP_CURSOR_AFTER_HANDLER = 1338\nSP_CASE_NOT_FOUND = 1339\nFPARSER_TOO_BIG_FILE = 1340\nFPARSER_BAD_HEADER = 1341\nFPARSER_EOF_IN_COMMENT = 1342\nFPARSER_ERROR_IN_PARAMETER = 1343\nFPARSER_EOF_IN_UNKNOWN_PARAMETER = 1344\nVIEW_NO_EXPLAIN = 1345\nFRM_UNKNOWN_TYPE = 1346\nWRONG_OBJECT = 1347\nNONUPDATEABLE_COLUMN = 1348\nVIEW_SELECT_DERIVED = 1349\nVIEW_SELECT_CLAUSE = 1350\nVIEW_SELECT_VARIABLE = 1351\nVIEW_SELECT_TMPTABLE = 1352\nVIEW_WRONG_LIST = 1353\nWARN_VIEW_MERGE = 1354\nWARN_VIEW_WITHOUT_KEY = 1355\nVIEW_INVALID = 1356\nSP_NO_DROP_SP = 1357\nSP_GOTO_IN_HNDLR = 1358\nTRG_ALREADY_EXISTS = 1359\nTRG_DOES_NOT_EXIST = 1360\nTRG_ON_VIEW_OR_TEMP_TABLE = 1361\nTRG_CANT_CHANGE_ROW = 1362\nTRG_NO_SUCH_ROW_IN_TRG = 1363\nNO_DEFAULT_FOR_FIELD = 1364\nDIVISION_BY_ZERO = 1365\nTRUNCATED_WRONG_VALUE_FOR_FIELD = 1366\nILLEGAL_VALUE_FOR_TYPE = 1367\nVIEW_NONUPD_CHECK = 1368\nVIEW_CHECK_FAILED = 1369\nPROCACCESS_DENIED_ERROR = 1370\nRELAY_LOG_FAIL = 1371\nPASSWD_LENGTH = 1372\nUNKNOWN_TARGET_BINLOG = 1373\nIO_ERR_LOG_INDEX_READ = 1374\nBINLOG_PURGE_PROHIBITED = 1375\nFSEEK_FAIL = 1376\nBINLOG_PURGE_FATAL_ERR = 1377\nLOG_IN_USE = 1378\nLOG_PURGE_UNKNOWN_ERR = 1379\nRELAY_LOG_INIT = 1380\nNO_BINARY_LOGGING = 1381\nRESERVED_SYNTAX = 1382\nWSAS_FAILED = 1383\nDIFF_GROUPS_PROC = 1384\nNO_GROUP_FOR_PROC = 1385\nORDER_WITH_PROC = 1386\nLOGGING_PROHIBIT_CHANGING_OF = 1387\nNO_FILE_MAPPING = 1388\nWRONG_MAGIC = 1389\nPS_MANY_PARAM = 1390\nKEY_PART_0 = 1391\nVIEW_CHECKSUM = 1392\nVIEW_MULTIUPDATE = 1393\nVIEW_NO_INSERT_FIELD_LIST = 1394\nVIEW_DELETE_MERGE_VIEW = 1395\nCANNOT_USER = 1396\nXAER_NOTA = 1397\nXAER_INVAL = 1398\nXAER_RMFAIL = 1399\nXAER_OUTSIDE = 1400\nXAER_RMERR = 1401\nXA_RBROLLBACK = 1402\nNONEXISTING_PROC_GRANT = 1403\nPROC_AUTO_GRANT_FAIL = 1404\nPROC_AUTO_REVOKE_FAIL = 1405\nDATA_TOO_LONG = 1406\nSP_BAD_SQLSTATE = 1407\nSTARTUP = 1408\nLOAD_FROM_FIXED_SIZE_ROWS_TO_VAR = 1409\nCANT_CREATE_USER_WITH_GRANT = 1410\nWRONG_VALUE_FOR_TYPE = 1411\nTABLE_DEF_CHANGED = 1412\nSP_DUP_HANDLER = 1413\nSP_NOT_VAR_ARG = 1414\nSP_NO_RETSET = 1415\nCANT_CREATE_GEOMETRY_OBJECT = 1416\nFAILED_ROUTINE_BREAK_BINLOG = 1417\nBINLOG_UNSAFE_ROUTINE = 1418\nBINLOG_CREATE_ROUTINE_NEED_SUPER = 1419\nEXEC_STMT_WITH_OPEN_CURSOR = 1420\nSTMT_HAS_NO_OPEN_CURSOR = 1421\nCOMMIT_NOT_ALLOWED_IN_SF_OR_TRG = 1422\nNO_DEFAULT_FOR_VIEW_FIELD = 1423\nSP_NO_RECURSION = 1424\nTOO_BIG_SCALE = 1425\nTOO_BIG_PRECISION = 1426\nM_BIGGER_THAN_D = 1427\nWRONG_LOCK_OF_SYSTEM_TABLE = 1428\nCONNECT_TO_FOREIGN_DATA_SOURCE = 1429\nQUERY_ON_FOREIGN_DATA_SOURCE = 1430\nFOREIGN_DATA_SOURCE_DOESNT_EXIST = 1431\nFOREIGN_DATA_STRING_INVALID_CANT_CREATE = 1432\nFOREIGN_DATA_STRING_INVALID = 1433\nCANT_CREATE_FEDERATED_TABLE = 1434\nTRG_IN_WRONG_SCHEMA = 1435\nSTACK_OVERRUN_NEED_MORE = 1436\nTOO_LONG_BODY = 1437\nWARN_CANT_DROP_DEFAULT_KEYCACHE = 1438\nTOO_BIG_DISPLAYWIDTH = 1439\nXAER_DUPID = 1440\nDATETIME_FUNCTION_OVERFLOW = 1441\nCANT_UPDATE_USED_TABLE_IN_SF_OR_TRG = 1442\nVIEW_PREVENT_UPDATE = 1443\nPS_NO_RECURSION = 1444\nSP_CANT_SET_AUTOCOMMIT = 1445\nMALFORMED_DEFINER = 1446\nVIEW_FRM_NO_USER = 1447\nVIEW_OTHER_USER = 1448\nNO_SUCH_USER = 1449\nFORBID_SCHEMA_CHANGE = 1450\nROW_IS_REFERENCED_2 = 1451\nNO_REFERENCED_ROW_2 = 1452\nSP_BAD_VAR_SHADOW = 1453\nTRG_NO_DEFINER = 1454\nOLD_FILE_FORMAT = 1455\nSP_RECURSION_LIMIT = 1456\nSP_PROC_TABLE_CORRUPT = 1457\nERROR_LAST = 1457\n\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/constants/FIELD_TYPE.py",
    "content": "\"\"\"MySQL FIELD_TYPE Constants\n\nThese constants represent the various column (field) types that are\nsupported by MySQL.\n\n\"\"\"\n\nDECIMAL = 0\nTINY = 1\nSHORT = 2\nLONG = 3\nFLOAT = 4\nDOUBLE = 5\nNULL = 6\nTIMESTAMP = 7\nLONGLONG = 8\nINT24 = 9\nDATE = 10\nTIME = 11\nDATETIME = 12\nYEAR = 13\nNEWDATE = 14\nVARCHAR = 15\nBIT = 16\nNEWDECIMAL = 246\nENUM = 247\nSET = 248\nTINY_BLOB = 249\nMEDIUM_BLOB = 250\nLONG_BLOB = 251\nBLOB = 252\nVAR_STRING = 253\nSTRING = 254\nGEOMETRY = 255\n\nCHAR = TINY\nINTERVAL = ENUM\t\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/constants/FLAG.py",
    "content": "\"\"\"MySQL FLAG Constants\n\nThese flags are used along with the FIELD_TYPE to indicate various\nproperties of columns in a result set.\n\n\"\"\"\n\nNOT_NULL = 1\nPRI_KEY = 2\nUNIQUE_KEY = 4\nMULTIPLE_KEY = 8\nBLOB = 16\nUNSIGNED = 32\nZEROFILL = 64\nBINARY = 128\nENUM = 256\nAUTO_INCREMENT = 512\nTIMESTAMP = 1024\nSET = 2048\nNUM = 32768\nPART_KEY = 16384\nGROUP = 32768\nUNIQUE = 65536\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/constants/REFRESH.py",
    "content": "\"\"\"MySQL REFRESH Constants\n\nThese constants seem to mostly deal with things internal to the\nMySQL server. Forget you saw this.\n\n\"\"\"\n\nGRANT = 1\nLOG = 2\nTABLES = 4\nHOSTS = 8\nSTATUS = 16\nTHREADS = 32\nSLAVE = 64\nMASTER = 128\nREAD_LOCK = 16384\nFAST = 32768\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/constants/__init__.py",
    "content": "__all__ = ['CR', 'FIELD_TYPE','CLIENT','REFRESH','ER','FLAG']\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/converters.py",
    "content": "\"\"\"MySQLdb type conversion module\n\nThis module handles all the type conversions for MySQL. If the default\ntype conversions aren't what you need, you can make your own. The\ndictionary conversions maps some kind of type to a conversion function\nwhich returns the corresponding value:\n\nKey: FIELD_TYPE.* (from MySQLdb.constants)\n\nConversion function:\n\n    Arguments: string\n\n    Returns: Python object\n\nKey: Python type object (from types) or class\n\nConversion function:\n\n    Arguments: Python object of indicated type or class AND \n               conversion dictionary\n\n    Returns: SQL literal value\n\n    Notes: Most conversion functions can ignore the dictionary, but\n           it is a required parameter. It is necessary for converting\n           things like sequences and instances.\n\nDon't modify conversions if you can avoid it. Instead, make copies\n(with the copy() method), modify the copies, and then pass them to\nMySQL.connect().\n\n\"\"\"\n\nfrom _mysql import string_literal, escape_sequence, escape_dict, escape, NULL\nfrom constants import FIELD_TYPE, FLAG\nfrom times import *\nimport types\nimport array\n\ntry:\n    set\nexcept NameError:\n    from sets import Set as set\n\ndef Bool2Str(s, d): return str(int(s))\n\ndef Str2Set(s):\n    return set([ i for i in s.split(',') if i ])\n\ndef Set2Str(s, d):\n    return string_literal(','.join(s), d)\n    \ndef Thing2Str(s, d):\n    \"\"\"Convert something into a string via str().\"\"\"\n    return str(s)\n\ndef Unicode2Str(s, d):\n    \"\"\"Convert a unicode object to a string using the default encoding.\n    This is only used as a placeholder for the real function, which\n    is connection-dependent.\"\"\"\n    return s.encode()\n\nLong2Int = Thing2Str\n\ndef Float2Str(o, d):\n    return '%.15g' % o\n\ndef None2NULL(o, d):\n    \"\"\"Convert None to NULL.\"\"\"\n    return NULL # duh\n\ndef Thing2Literal(o, d):\n    \n    \"\"\"Convert something into a SQL string literal.  If using\n    MySQL-3.23 or newer, string_literal() is a method of the\n    _mysql.MYSQL object, and this function will be overridden with\n    that method when the connection is created.\"\"\"\n\n    return string_literal(o, d)\n\n\ndef Instance2Str(o, d):\n\n    \"\"\"\n\n    Convert an Instance to a string representation.  If the __str__()\n    method produces acceptable output, then you don't need to add the\n    class to conversions; it will be handled by the default\n    converter. If the exact class is not found in d, it will use the\n    first class it can find for which o is an instance.\n\n    \"\"\"\n\n    if d.has_key(o.__class__):\n        return d[o.__class__](o, d)\n    cl = filter(lambda x,o=o:\n                type(x) is types.ClassType\n                and isinstance(o, x), d.keys())\n    if not cl and hasattr(types, 'ObjectType'):\n        cl = filter(lambda x,o=o:\n                    type(x) is types.TypeType\n                    and isinstance(o, x)\n                    and d[x] is not Instance2Str,\n                    d.keys())\n    if not cl:\n        return d[types.StringType](o,d)\n    d[o.__class__] = d[cl[0]]\n    return d[cl[0]](o, d)\n\ndef char_array(s):\n    return array.array('c', s)\n\ndef array2Str(o, d):\n    return Thing2Literal(o.tostring(), d)\n\nconversions = {\n    types.IntType: Thing2Str,\n    types.LongType: Long2Int,\n    types.FloatType: Float2Str,\n    types.NoneType: None2NULL,\n    types.TupleType: escape_sequence,\n    types.ListType: escape_sequence,\n    types.DictType: escape_dict,\n    types.InstanceType: Instance2Str,\n    array.ArrayType: array2Str,\n    types.StringType: Thing2Literal, # default\n    types.UnicodeType: Unicode2Str,\n    types.ObjectType: Instance2Str,\n    types.BooleanType: Bool2Str,\n    DateTimeType: DateTime2literal,\n    DateTimeDeltaType: DateTimeDelta2literal,\n    set: Set2Str,\n    FIELD_TYPE.TINY: int,\n    FIELD_TYPE.SHORT: int,\n    FIELD_TYPE.LONG: long,\n    FIELD_TYPE.FLOAT: float,\n    FIELD_TYPE.DOUBLE: float,\n    FIELD_TYPE.DECIMAL: float,\n    FIELD_TYPE.NEWDECIMAL: float,\n    FIELD_TYPE.LONGLONG: long,\n    FIELD_TYPE.INT24: int,\n    FIELD_TYPE.YEAR: int,\n    FIELD_TYPE.SET: Str2Set,\n    FIELD_TYPE.TIMESTAMP: mysql_timestamp_converter,\n    FIELD_TYPE.DATETIME: DateTime_or_None,\n    FIELD_TYPE.TIME: TimeDelta_or_None,\n    FIELD_TYPE.DATE: Date_or_None,\n    FIELD_TYPE.BLOB: [\n        (FLAG.BINARY, str),\n        ],\n    FIELD_TYPE.STRING: [\n        (FLAG.BINARY, str),\n        ],\n    FIELD_TYPE.VAR_STRING: [\n        (FLAG.BINARY, str),\n        ],\n    FIELD_TYPE.VARCHAR: [\n        (FLAG.BINARY, str),\n        ],\n    }\n\ntry:\n    from decimal import Decimal\n    conversions[FIELD_TYPE.DECIMAL] = Decimal\n    conversions[FIELD_TYPE.NEWDECIMAL] = Decimal\nexcept ImportError:\n    pass\n\n\n\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/cursors.py",
    "content": "\"\"\"MySQLdb Cursors\n\nThis module implements Cursors of various types for MySQLdb. By\ndefault, MySQLdb uses the Cursor class.\n\n\"\"\"\n\nimport re\nimport sys\nfrom types import ListType, TupleType, UnicodeType\n\n\nrestr = (r\"\\svalues\\s*\"\n        r\"(\\(((?<!\\\\)'[^\\)]*?\\)[^\\)]*(?<!\\\\)?'\"\n        r\"|[^\\(\\)]|\"\n        r\"(?:\\([^\\)]*\\))\"\n        r\")+\\))\")\n\ninsert_values= re.compile(restr)\nfrom _mysql_exceptions import Warning, Error, InterfaceError, DataError, \\\n     DatabaseError, OperationalError, IntegrityError, InternalError, \\\n     NotSupportedError, ProgrammingError\n\n\nclass BaseCursor(object):\n    \n    \"\"\"A base for Cursor classes. Useful attributes:\n    \n    description\n        A tuple of DB API 7-tuples describing the columns in\n        the last executed query; see PEP-249 for details.\n\n    description_flags\n        Tuple of column flags for last query, one entry per column\n        in the result set. Values correspond to those in\n        MySQLdb.constants.FLAG. See MySQL documentation (C API)\n        for more information. Non-standard extension.\n    \n    arraysize\n        default number of rows fetchmany() will fetch\n\n    \"\"\"\n\n    from _mysql_exceptions import MySQLError, Warning, Error, InterfaceError, \\\n         DatabaseError, DataError, OperationalError, IntegrityError, \\\n         InternalError, ProgrammingError, NotSupportedError\n    \n    _defer_warnings = False\n    \n    def __init__(self, connection):\n        from weakref import proxy\n    \n        self.connection = proxy(connection)\n        self.description = None\n        self.description_flags = None\n        self.rowcount = -1\n        self.arraysize = 1\n        self._executed = None\n        self.lastrowid = None\n        self.messages = []\n        self.errorhandler = connection.errorhandler\n        self._result = None\n        self._warnings = 0\n        self._info = None\n        self.rownumber = None\n        \n    def __del__(self):\n        self.close()\n        self.errorhandler = None\n        self._result = None\n\n    def close(self):\n        \"\"\"Close the cursor. No further queries will be possible.\"\"\"\n        if not self.connection: return\n        while self.nextset(): pass\n        self.connection = None\n\n    def _check_executed(self):\n        if not self._executed:\n            self.errorhandler(self, ProgrammingError, \"execute() first\")\n\n    def _warning_check(self):\n        from warnings import warn\n        if self._warnings:\n            warnings = self._get_db().show_warnings()\n            if warnings:\n                # This is done in two loops in case\n                # Warnings are set to raise exceptions.\n                for w in warnings:\n                    self.messages.append((self.Warning, w))\n                for w in warnings:\n                    warn(w[-1], self.Warning, 3)\n            elif self._info:\n                self.messages.append((self.Warning, self._info))\n                warn(self._info, self.Warning, 3)\n\n    def nextset(self):\n        \"\"\"Advance to the next result set.\n\n        Returns None if there are no more result sets.\n        \"\"\"\n        if self._executed:\n            self.fetchall()\n        del self.messages[:]\n        \n        db = self._get_db()\n        nr = db.next_result()\n        if nr == -1:\n            return None\n        self._do_get_result()\n        self._post_get_result()\n        self._warning_check()\n        return 1\n\n    def _post_get_result(self): pass\n    \n    def _do_get_result(self):\n        db = self._get_db()\n        self._result = self._get_result()\n        self.rowcount = db.affected_rows()\n        self.rownumber = 0\n        self.description = self._result and self._result.describe() or None\n        self.description_flags = self._result and self._result.field_flags() or None\n        self.lastrowid = db.insert_id()\n        self._warnings = db.warning_count()\n        self._info = db.info()\n    \n    def setinputsizes(self, *args):\n        \"\"\"Does nothing, required by DB API.\"\"\"\n      \n    def setoutputsizes(self, *args):\n        \"\"\"Does nothing, required by DB API.\"\"\"\n\n    def _get_db(self):\n        if not self.connection:\n            self.errorhandler(self, ProgrammingError, \"cursor closed\")\n        return self.connection\n    \n    def execute(self, query, args=None):\n\n        \"\"\"Execute a query.\n        \n        query -- string, query to execute on server\n        args -- optional sequence or mapping, parameters to use with query.\n\n        Note: If args is a sequence, then %s must be used as the\n        parameter placeholder in the query. If a mapping is used,\n        %(key)s must be used as the placeholder.\n\n        Returns long integer rows affected, if any\n\n        \"\"\"\n        del self.messages[:]\n        db = self._get_db()\n        charset = db.character_set_name()\n        if isinstance(query, unicode):\n            query = query.encode(charset)\n        if args is not None:\n            query = query % db.literal(args)\n        try:\n            r = self._query(query)\n        except TypeError, m:\n            if m.args[0] in (\"not enough arguments for format string\",\n                             \"not all arguments converted\"):\n                self.messages.append((ProgrammingError, m.args[0]))\n                self.errorhandler(self, ProgrammingError, m.args[0])\n            else:\n                self.messages.append((TypeError, m))\n                self.errorhandler(self, TypeError, m)\n        except:\n            exc, value, tb = sys.exc_info()\n            del tb\n            self.messages.append((exc, value))\n            self.errorhandler(self, exc, value)\n        self._executed = query\n        if not self._defer_warnings: self._warning_check()\n        return r\n\n    def executemany(self, query, args):\n\n        \"\"\"Execute a multi-row query.\n        \n        query -- string, query to execute on server\n\n        args\n\n            Sequence of sequences or mappings, parameters to use with\n            query.\n            \n        Returns long integer rows affected, if any.\n        \n        This method improves performance on multiple-row INSERT and\n        REPLACE. Otherwise it is equivalent to looping over args with\n        execute().\n\n        \"\"\"\n        del self.messages[:]\n        db = self._get_db()\n        if not args: return\n        charset = db.character_set_name()\n        if isinstance(query, unicode): query = query.encode(charset)\n        m = insert_values.search(query)\n        if not m:\n            r = 0\n            for a in args:\n                r = r + self.execute(query, a)\n            return r\n        p = m.start(1)\n        e = m.end(1)\n        qv = m.group(1)\n        try:\n            q = [ qv % db.literal(a) for a in args ]\n        except TypeError, msg:\n            if msg.args[0] in (\"not enough arguments for format string\",\n                               \"not all arguments converted\"):\n                self.errorhandler(self, ProgrammingError, msg.args[0])\n            else:\n                self.errorhandler(self, TypeError, msg)\n        except:\n            exc, value, tb = sys.exc_info()\n            del tb\n            self.errorhandler(self, exc, value)\n        r = self._query('\\n'.join([query[:p], ',\\n'.join(q), query[e:]]))\n        if not self._defer_warnings: self._warning_check()\n        return r\n    \n    def callproc(self, procname, args=()):\n\n        \"\"\"Execute stored procedure procname with args\n        \n        procname -- string, name of procedure to execute on server\n\n        args -- Sequence of parameters to use with procedure\n\n        Returns the original args.\n\n        Compatibility warning: PEP-249 specifies that any modified\n        parameters must be returned. This is currently impossible\n        as they are only available by storing them in a server\n        variable and then retrieved by a query. Since stored\n        procedures return zero or more result sets, there is no\n        reliable way to get at OUT or INOUT parameters via callproc.\n        The server variables are named @_procname_n, where procname\n        is the parameter above and n is the position of the parameter\n        (from zero). Once all result sets generated by the procedure\n        have been fetched, you can issue a SELECT @_procname_0, ...\n        query using .execute() to get any OUT or INOUT values.\n\n        Compatibility warning: The act of calling a stored procedure\n        itself creates an empty result set. This appears after any\n        result sets generated by the procedure. This is non-standard\n        behavior with respect to the DB-API. Be sure to use nextset()\n        to advance through all result sets; otherwise you may get\n        disconnected.\n        \"\"\"\n\n        db = self._get_db()\n        charset = db.character_set_name()\n        for index, arg in enumerate(args):\n            q = \"SET @_%s_%d=%s\" % (procname, index,\n                                         db.literal(arg))\n            if isinstance(q, unicode):\n                q = q.encode(charset)\n            self._query(q)\n            self.nextset()\n            \n        q = \"CALL %s(%s)\" % (procname,\n                             ','.join(['@_%s_%d' % (procname, i)\n                                       for i in range(len(args))]))\n        if type(q) is UnicodeType:\n            q = q.encode(charset)\n        self._query(q)\n        self._executed = q\n        if not self._defer_warnings: self._warning_check()\n        return args\n    \n    def _do_query(self, q):\n        db = self._get_db()\n        self._last_executed = q\n        db.query(q)\n        self._do_get_result()\n        return self.rowcount\n\n    def _query(self, q): return self._do_query(q)\n    \n    def _fetch_row(self, size=1):\n        if not self._result:\n            return ()\n        return self._result.fetch_row(size, self._fetch_type)\n\n    def __iter__(self):\n        return iter(self.fetchone, None)\n\n    Warning = Warning\n    Error = Error\n    InterfaceError = InterfaceError\n    DatabaseError = DatabaseError\n    DataError = DataError\n    OperationalError = OperationalError\n    IntegrityError = IntegrityError\n    InternalError = InternalError\n    ProgrammingError = ProgrammingError\n    NotSupportedError = NotSupportedError\n   \n\nclass CursorStoreResultMixIn(object):\n\n    \"\"\"This is a MixIn class which causes the entire result set to be\n    stored on the client side, i.e. it uses mysql_store_result(). If the\n    result set can be very large, consider adding a LIMIT clause to your\n    query, or using CursorUseResultMixIn instead.\"\"\"\n\n    def _get_result(self): return self._get_db().store_result()\n\n    def _query(self, q):\n        rowcount = self._do_query(q)\n        self._post_get_result()\n        return rowcount\n\n    def _post_get_result(self):\n        self._rows = self._fetch_row(0)\n        self._result = None\n\n    def fetchone(self):\n        \"\"\"Fetches a single row from the cursor. None indicates that\n        no more rows are available.\"\"\"\n        self._check_executed()\n        if self.rownumber >= len(self._rows): return None\n        result = self._rows[self.rownumber]\n        self.rownumber = self.rownumber+1\n        return result\n\n    def fetchmany(self, size=None):\n        \"\"\"Fetch up to size rows from the cursor. Result set may be smaller\n        than size. If size is not defined, cursor.arraysize is used.\"\"\"\n        self._check_executed()\n        end = self.rownumber + (size or self.arraysize)\n        result = self._rows[self.rownumber:end]\n        self.rownumber = min(end, len(self._rows))\n        return result\n\n    def fetchall(self):\n        \"\"\"Fetchs all available rows from the cursor.\"\"\"\n        self._check_executed()\n        if self.rownumber:\n            result = self._rows[self.rownumber:]\n        else:\n            result = self._rows\n        self.rownumber = len(self._rows)\n        return result\n    \n    def scroll(self, value, mode='relative'):\n        \"\"\"Scroll the cursor in the result set to a new position according\n        to mode.\n        \n        If mode is 'relative' (default), value is taken as offset to\n        the current position in the result set, if set to 'absolute',\n        value states an absolute target position.\"\"\"\n        self._check_executed()\n        if mode == 'relative':\n            r = self.rownumber + value\n        elif mode == 'absolute':\n            r = value\n        else:\n            self.errorhandler(self, ProgrammingError,\n                              \"unknown scroll mode %s\" % `mode`)\n        if r < 0 or r >= len(self._rows):\n            self.errorhandler(self, IndexError, \"out of range\")\n        self.rownumber = r\n\n    def __iter__(self):\n        self._check_executed()\n        result = self.rownumber and self._rows[self.rownumber:] or self._rows\n        return iter(result)\n    \n\nclass CursorUseResultMixIn(object):\n\n    \"\"\"This is a MixIn class which causes the result set to be stored\n    in the server and sent row-by-row to client side, i.e. it uses\n    mysql_use_result(). You MUST retrieve the entire result set and\n    close() the cursor before additional queries can be peformed on\n    the connection.\"\"\"\n\n    _defer_warnings = True\n    \n    def _get_result(self): return self._get_db().use_result()\n\n    def fetchone(self):\n        \"\"\"Fetches a single row from the cursor.\"\"\"\n        self._check_executed()\n        r = self._fetch_row(1)\n        if not r:\n            self._warning_check()\n            return None\n        self.rownumber = self.rownumber + 1\n        return r[0]\n             \n    def fetchmany(self, size=None):\n        \"\"\"Fetch up to size rows from the cursor. Result set may be smaller\n        than size. If size is not defined, cursor.arraysize is used.\"\"\"\n        self._check_executed()\n        r = self._fetch_row(size or self.arraysize)\n        self.rownumber = self.rownumber + len(r)\n        if not r:\n            self._warning_check()\n        return r\n         \n    def fetchall(self):\n        \"\"\"Fetchs all available rows from the cursor.\"\"\"\n        self._check_executed()\n        r = self._fetch_row(0)\n        self.rownumber = self.rownumber + len(r)\n        self._warning_check()\n        return r\n\n    def __iter__(self):\n        return self\n\n    def next(self):\n        row = self.fetchone()\n        if row is None:\n            raise StopIteration\n        return row\n    \n\nclass CursorTupleRowsMixIn(object):\n\n    \"\"\"This is a MixIn class that causes all rows to be returned as tuples,\n    which is the standard form required by DB API.\"\"\"\n\n    _fetch_type = 0\n\n\nclass CursorDictRowsMixIn(object):\n\n    \"\"\"This is a MixIn class that causes all rows to be returned as\n    dictionaries. This is a non-standard feature.\"\"\"\n\n    _fetch_type = 1\n\n    def fetchoneDict(self):\n        \"\"\"Fetch a single row as a dictionary. Deprecated:\n        Use fetchone() instead. Will be removed in 1.3.\"\"\"\n        from warnings import warn\n        warn(\"fetchoneDict() is non-standard and will be removed in 1.3\",\n             DeprecationWarning, 2)\n        return self.fetchone()\n\n    def fetchmanyDict(self, size=None):\n        \"\"\"Fetch several rows as a list of dictionaries. Deprecated:\n        Use fetchmany() instead. Will be removed in 1.3.\"\"\"\n        from warnings import warn\n        warn(\"fetchmanyDict() is non-standard and will be removed in 1.3\",\n             DeprecationWarning, 2)\n        return self.fetchmany(size)\n\n    def fetchallDict(self):\n        \"\"\"Fetch all available rows as a list of dictionaries. Deprecated:\n        Use fetchall() instead. Will be removed in 1.3.\"\"\"\n        from warnings import warn\n        warn(\"fetchallDict() is non-standard and will be removed in 1.3\",\n             DeprecationWarning, 2)\n        return self.fetchall()\n\n\nclass CursorOldDictRowsMixIn(CursorDictRowsMixIn):\n\n    \"\"\"This is a MixIn class that returns rows as dictionaries with\n    the same key convention as the old Mysqldb (MySQLmodule). Don't\n    use this.\"\"\"\n\n    _fetch_type = 2\n\n\nclass Cursor(CursorStoreResultMixIn, CursorTupleRowsMixIn,\n             BaseCursor):\n\n    \"\"\"This is the standard Cursor class that returns rows as tuples\n    and stores the result set in the client.\"\"\"\n\n\nclass DictCursor(CursorStoreResultMixIn, CursorDictRowsMixIn,\n                 BaseCursor):\n\n     \"\"\"This is a Cursor class that returns rows as dictionaries and\n    stores the result set in the client.\"\"\"\n   \n\nclass SSCursor(CursorUseResultMixIn, CursorTupleRowsMixIn,\n               BaseCursor):\n\n    \"\"\"This is a Cursor class that returns rows as tuples and stores\n    the result set in the server.\"\"\"\n\n\nclass SSDictCursor(CursorUseResultMixIn, CursorDictRowsMixIn,\n                   BaseCursor):\n\n    \"\"\"This is a Cursor class that returns rows as dictionaries and\n    stores the result set in the server.\"\"\"\n\n\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/monkey.py",
    "content": "\n# Copyright (C) 2012-2013 SINA, All rights reserved.\n\ndef patch():\n    import sys\n\n    if  'MySQLdb' in sys.modules:\n        import warnings\n        warnings.warn('MySQLdb has alreay been imported', Warning)\n\n    modules_to_replace = (\n        'MySQLdb',\n        'MySQLdb.release',\n        'MySQLdb.connections',\n        'MySQLdb.cursors',\n        'MySQLdb.converters',\n        'MySQLdb.constants',\n        'MySQLdb.constants.CLIENT',\n        'MySQLdb.constants.FIELD_TYPE',\n        'MySQLdb.constants.FLAG',\n    )\n\n    for name in modules_to_replace:\n        if name in sys.modules:\n            sys.modules.pop(name)\n\n    import sae._restful_mysql\n    from sae._restful_mysql import _mysql, _mysql_exceptions\n    sys.modules['MySQLdb'] = sae._restful_mysql\n    sys.modules['_mysql'] = _mysql\n    sys.modules['_mysql_exceptions'] = _mysql_exceptions\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/release.py",
    "content": "\n__author__ = \"Andy Dustman <adustman@users.sourceforge.net>\"\nversion_info = (1,2,3,'final',0)\n__version__ = \"1.2.3\"\n"
  },
  {
    "path": "dev_server/sae/_restful_mysql/times.py",
    "content": "\"\"\"times module\n\nThis module provides some Date and Time classes for dealing with MySQL data.\n\nUse Python datetime module to handle date and time columns.\"\"\"\n\nimport math\nfrom time import localtime\nfrom datetime import date, datetime, time, timedelta\nfrom _mysql import string_literal\n\nDate = date\nTime = time\nTimeDelta = timedelta\nTimestamp = datetime\n\nDateTimeDeltaType = timedelta\nDateTimeType = datetime\n\ndef DateFromTicks(ticks):\n    \"\"\"Convert UNIX ticks into a date instance.\"\"\"\n    return date(*localtime(ticks)[:3])\n\ndef TimeFromTicks(ticks):\n    \"\"\"Convert UNIX ticks into a time instance.\"\"\"\n    return time(*localtime(ticks)[3:6])\n\ndef TimestampFromTicks(ticks):\n    \"\"\"Convert UNIX ticks into a datetime instance.\"\"\"\n    return datetime(*localtime(ticks)[:6])\n\nformat_TIME = format_DATE = str\n\ndef format_TIMEDELTA(v):\n    seconds = int(v.seconds) % 60\n    minutes = int(v.seconds / 60) % 60\n    hours = int(v.seconds / 3600) % 24\n    return '%d %d:%d:%d' % (v.days, hours, minutes, seconds)\n\ndef format_TIMESTAMP(d):\n    return d.strftime(\"%Y-%m-%d %H:%M:%S\")\n\n\ndef DateTime_or_None(s):\n    if ' ' in s:\n        sep = ' '\n    elif 'T' in s:\n        sep = 'T'\n    else:\n        return Date_or_None(s)\n\n    try:\n        d, t = s.split(sep, 1)\n        return datetime(*[ int(x) for x in d.split('-')+t.split(':') ])\n    except:\n        return Date_or_None(s)\n\ndef TimeDelta_or_None(s):\n    try:\n        h, m, s = s.split(':')\n        h, m, s = int(h), int(m), float(s)\n        td = timedelta(hours=abs(h), minutes=m, seconds=int(s),\n                       microseconds=int(math.modf(s)[0] * 1000000))\n        if h < 0:\n            return -td\n        else:\n            return td\n    except ValueError:\n        # unpacking or int/float conversion failed\n        return None\n\ndef Time_or_None(s):\n    try:\n        h, m, s = s.split(':')\n        h, m, s = int(h), int(m), float(s)\n        return time(hour=h, minute=m, second=int(s),\n                    microsecond=int(math.modf(s)[0] * 1000000))\n    except ValueError:\n        return None\n\ndef Date_or_None(s):\n    try: return date(*[ int(x) for x in s.split('-',2)])\n    except: return None\n\ndef DateTime2literal(d, c):\n    \"\"\"Format a DateTime object as an ISO timestamp.\"\"\"\n    return string_literal(format_TIMESTAMP(d),c)\n    \ndef DateTimeDelta2literal(d, c):\n    \"\"\"Format a DateTimeDelta object as a time.\"\"\"\n    return string_literal(format_TIMEDELTA(d),c)\n\ndef mysql_timestamp_converter(s):\n    \"\"\"Convert a MySQL TIMESTAMP to a Timestamp object.\"\"\"\n    # MySQL>4.1 returns TIMESTAMP in the same format as DATETIME\n    if s[4] == '-': return DateTime_or_None(s)\n    s = s + \"0\"*(14-len(s)) # padding\n    parts = map(int, filter(None, (s[:4],s[4:6],s[6:8],\n                                   s[8:10],s[10:12],s[12:14])))\n    try: return Timestamp(*parts)\n    except: return None\n"
  },
  {
    "path": "dev_server/sae/channel.js",
    "content": "(function(){var COMPILED=!0,goog=goog||{};goog.global=this;goog.exportPath_=function(a,b,c){a=a.split(\".\");c=c||goog.global;a[0]in c||!c.execScript||c.execScript(\"var \"+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c=c[d]?c[d]:c[d]={}:c[d]=b};goog.define=function(a,b){var c=b;COMPILED||goog.global.CLOSURE_DEFINES&&Object.prototype.hasOwnProperty.call(goog.global.CLOSURE_DEFINES,a)&&(c=goog.global.CLOSURE_DEFINES[a]);goog.exportPath_(a,c)};goog.DEBUG=!0;goog.LOCALE=\"en\";goog.TRUSTED_SITE=!0;\ngoog.provide=function(a){if(!COMPILED){if(goog.isProvided_(a))throw Error('Namespace \"'+a+'\" already declared.');delete goog.implicitNamespaces_[a];for(var b=a;(b=b.substring(0,b.lastIndexOf(\".\")))&&!goog.getObjectByName(b);)goog.implicitNamespaces_[b]=!0}goog.exportPath_(a)};goog.setTestOnly=function(a){if(COMPILED&&!goog.DEBUG)throw a=a||\"\",Error(\"Importing test-only code into non-debug environment\"+a?\": \"+a:\".\");};\nCOMPILED||(goog.isProvided_=function(a){return!goog.implicitNamespaces_[a]&&!!goog.getObjectByName(a)},goog.implicitNamespaces_={});goog.getObjectByName=function(a,b){for(var c=a.split(\".\"),d=b||goog.global,e;e=c.shift();)if(goog.isDefAndNotNull(d[e]))d=d[e];else return null;return d};goog.globalize=function(a,b){var c=b||goog.global,d;for(d in a)c[d]=a[d]};\ngoog.addDependency=function(a,b,c){if(goog.DEPENDENCIES_ENABLED){var d;a=a.replace(/\\\\/g,\"/\");for(var e=goog.dependencies_,f=0;d=b[f];f++)e.nameToPath[d]=a,a in e.pathToNames||(e.pathToNames[a]={}),e.pathToNames[a][d]=!0;for(d=0;b=c[d];d++)a in e.requires||(e.requires[a]={}),e.requires[a][b]=!0}};goog.ENABLE_DEBUG_LOADER=!0;\ngoog.require=function(a){if(!COMPILED&&!goog.isProvided_(a)){if(goog.ENABLE_DEBUG_LOADER){var b=goog.getPathFromDeps_(a);if(b){goog.included_[b]=!0;goog.writeScripts_();return}}a=\"goog.require could not find: \"+a;goog.global.console&&goog.global.console.error(a);throw Error(a);}};goog.basePath=\"\";goog.nullFunction=function(){};goog.identityFunction=function(a,b){return a};goog.abstractMethod=function(){throw Error(\"unimplemented abstract method\");};\ngoog.addSingletonGetter=function(a){a.getInstance=function(){if(a.instance_)return a.instance_;goog.DEBUG&&(goog.instantiatedSingletons_[goog.instantiatedSingletons_.length]=a);return a.instance_=new a}};goog.instantiatedSingletons_=[];goog.DEPENDENCIES_ENABLED=!COMPILED&&goog.ENABLE_DEBUG_LOADER;\ngoog.DEPENDENCIES_ENABLED&&(goog.included_={},goog.dependencies_={pathToNames:{},nameToPath:{},requires:{},visited:{},written:{}},goog.inHtmlDocument_=function(){var a=goog.global.document;return\"undefined\"!=typeof a&&\"write\"in a},goog.findBasePath_=function(){if(goog.global.CLOSURE_BASE_PATH)goog.basePath=goog.global.CLOSURE_BASE_PATH;else if(goog.inHtmlDocument_())for(var a=goog.global.document.getElementsByTagName(\"script\"),b=a.length-1;0<=b;--b){var c=a[b].src,d=c.lastIndexOf(\"?\"),d=-1==d?c.length:\nd;if(\"base.js\"==c.substr(d-7,7)){goog.basePath=c.substr(0,d-7);break}}},goog.importScript_=function(a){var b=goog.global.CLOSURE_IMPORT_SCRIPT||goog.writeScriptTag_;!goog.dependencies_.written[a]&&b(a)&&(goog.dependencies_.written[a]=!0)},goog.writeScriptTag_=function(a){if(goog.inHtmlDocument_()){var b=goog.global.document;if(\"complete\"==b.readyState){if(/\\bdeps.js$/.test(a))return!1;throw Error('Cannot write \"'+a+'\" after document load');}b.write('<script type=\"text/javascript\" src=\"'+a+'\">\\x3c/script>');\nreturn!0}return!1},goog.writeScripts_=function(){function a(e){if(!(e in d.written)){if(!(e in d.visited)&&(d.visited[e]=!0,e in d.requires))for(var g in d.requires[e])if(!goog.isProvided_(g))if(g in d.nameToPath)a(d.nameToPath[g]);else throw Error(\"Undefined nameToPath for \"+g);e in c||(c[e]=!0,b.push(e))}}var b=[],c={},d=goog.dependencies_,e;for(e in goog.included_)d.written[e]||a(e);for(e=0;e<b.length;e++)if(b[e])goog.importScript_(goog.basePath+b[e]);else throw Error(\"Undefined script input\");\n},goog.getPathFromDeps_=function(a){return a in goog.dependencies_.nameToPath?goog.dependencies_.nameToPath[a]:null},goog.findBasePath_(),goog.global.CLOSURE_NO_DEPS||goog.importScript_(goog.basePath+\"deps.js\"));\ngoog.typeOf=function(a){var b=typeof a;if(\"object\"==b)if(a){if(a instanceof Array)return\"array\";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if(\"[object Window]\"==c)return\"object\";if(\"[object Array]\"==c||\"number\"==typeof a.length&&\"undefined\"!=typeof a.splice&&\"undefined\"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable(\"splice\"))return\"array\";if(\"[object Function]\"==c||\"undefined\"!=typeof a.call&&\"undefined\"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable(\"call\"))return\"function\"}else return\"null\";\nelse if(\"function\"==b&&\"undefined\"==typeof a.call)return\"object\";return b};goog.isDef=function(a){return void 0!==a};goog.isNull=function(a){return null===a};goog.isDefAndNotNull=function(a){return null!=a};goog.isArray=function(a){return\"array\"==goog.typeOf(a)};goog.isArrayLike=function(a){var b=goog.typeOf(a);return\"array\"==b||\"object\"==b&&\"number\"==typeof a.length};goog.isDateLike=function(a){return goog.isObject(a)&&\"function\"==typeof a.getFullYear};goog.isString=function(a){return\"string\"==typeof a};\ngoog.isBoolean=function(a){return\"boolean\"==typeof a};goog.isNumber=function(a){return\"number\"==typeof a};goog.isFunction=function(a){return\"function\"==goog.typeOf(a)};goog.isObject=function(a){var b=typeof a;return\"object\"==b&&null!=a||\"function\"==b};goog.getUid=function(a){return a[goog.UID_PROPERTY_]||(a[goog.UID_PROPERTY_]=++goog.uidCounter_)};goog.hasUid=function(a){return!!a[goog.UID_PROPERTY_]};goog.removeUid=function(a){\"removeAttribute\"in a&&a.removeAttribute(goog.UID_PROPERTY_);try{delete a[goog.UID_PROPERTY_]}catch(b){}};\ngoog.UID_PROPERTY_=\"closure_uid_\"+(1E9*Math.random()>>>0);goog.uidCounter_=0;goog.getHashCode=goog.getUid;goog.removeHashCode=goog.removeUid;goog.cloneObject=function(a){var b=goog.typeOf(a);if(\"object\"==b||\"array\"==b){if(a.clone)return a.clone();var b=\"array\"==b?[]:{},c;for(c in a)b[c]=goog.cloneObject(a[c]);return b}return a};goog.bindNative_=function(a,b,c){return a.call.apply(a.bind,arguments)};\ngoog.bindJs_=function(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}};goog.bind=function(a,b,c){Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf(\"native code\")?goog.bind=goog.bindNative_:goog.bind=goog.bindJs_;return goog.bind.apply(null,arguments)};\ngoog.partial=function(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var b=c.slice();b.push.apply(b,arguments);return a.apply(this,b)}};goog.mixin=function(a,b){for(var c in b)a[c]=b[c]};goog.now=goog.TRUSTED_SITE&&Date.now||function(){return+new Date};\ngoog.globalEval=function(a){if(goog.global.execScript)goog.global.execScript(a,\"JavaScript\");else if(goog.global.eval)if(null==goog.evalWorksForGlobals_&&(goog.global.eval(\"var _et_ = 1;\"),\"undefined\"!=typeof goog.global._et_?(delete goog.global._et_,goog.evalWorksForGlobals_=!0):goog.evalWorksForGlobals_=!1),goog.evalWorksForGlobals_)goog.global.eval(a);else{var b=goog.global.document,c=b.createElement(\"script\");c.type=\"text/javascript\";c.defer=!1;c.appendChild(b.createTextNode(a));b.body.appendChild(c);\nb.body.removeChild(c)}else throw Error(\"goog.globalEval not available\");};goog.evalWorksForGlobals_=null;goog.getCssName=function(a,b){var c=function(a){return goog.cssNameMapping_[a]||a},d=function(a){a=a.split(\"-\");for(var b=[],d=0;d<a.length;d++)b.push(c(a[d]));return b.join(\"-\")},d=goog.cssNameMapping_?\"BY_WHOLE\"==goog.cssNameMappingStyle_?c:d:function(a){return a};return b?a+\"-\"+d(b):d(a)};goog.setCssNameMapping=function(a,b){goog.cssNameMapping_=a;goog.cssNameMappingStyle_=b};\n!COMPILED&&goog.global.CLOSURE_CSS_NAME_MAPPING&&(goog.cssNameMapping_=goog.global.CLOSURE_CSS_NAME_MAPPING);goog.getMsg=function(a,b){var c=b||{},d;for(d in c){var e=(\"\"+c[d]).replace(/\\$/g,\"$$$$\");a=a.replace(RegExp(\"\\\\{\\\\$\"+d+\"\\\\}\",\"gi\"),e)}return a};goog.getMsgWithFallback=function(a,b){return a};goog.exportSymbol=function(a,b,c){goog.exportPath_(a,b,c)};goog.exportProperty=function(a,b,c){a[b]=c};\ngoog.inherits=function(a,b){function c(){}c.prototype=b.prototype;a.superClass_=b.prototype;a.prototype=new c;a.prototype.constructor=a};\ngoog.base=function(a,b,c){var d=arguments.callee.caller;if(goog.DEBUG&&!d)throw Error(\"arguments.caller not defined.  goog.base() expects not to be running in strict mode. See http://www.ecma-international.org/ecma-262/5.1/#sec-C\");if(d.superClass_)return d.superClass_.constructor.apply(a,Array.prototype.slice.call(arguments,1));for(var e=Array.prototype.slice.call(arguments,2),f=!1,g=a.constructor;g;g=g.superClass_&&g.superClass_.constructor)if(g.prototype[b]===d)f=!0;else if(f)return g.prototype[b].apply(a,\ne);if(a[b]===d)return a.constructor.prototype[b].apply(a,e);throw Error(\"goog.base called from a method of one name to a method of a different name\");};goog.scope=function(a){a.call(goog.global)};goog.debug={};goog.debug.Error=function(a){Error.captureStackTrace?Error.captureStackTrace(this,goog.debug.Error):this.stack=Error().stack||\"\";a&&(this.message=String(a))};goog.inherits(goog.debug.Error,Error);goog.debug.Error.prototype.name=\"CustomError\";goog.dom={};goog.dom.NodeType={ELEMENT:1,ATTRIBUTE:2,TEXT:3,CDATA_SECTION:4,ENTITY_REFERENCE:5,ENTITY:6,PROCESSING_INSTRUCTION:7,COMMENT:8,DOCUMENT:9,DOCUMENT_TYPE:10,DOCUMENT_FRAGMENT:11,NOTATION:12};goog.string={};goog.string.Unicode={NBSP:\"\\u00a0\"};goog.string.startsWith=function(a,b){return 0==a.lastIndexOf(b,0)};goog.string.endsWith=function(a,b){var c=a.length-b.length;return 0<=c&&a.indexOf(b,c)==c};goog.string.caseInsensitiveStartsWith=function(a,b){return 0==goog.string.caseInsensitiveCompare(b,a.substr(0,b.length))};goog.string.caseInsensitiveEndsWith=function(a,b){return 0==goog.string.caseInsensitiveCompare(b,a.substr(a.length-b.length,b.length))};\ngoog.string.caseInsensitiveEquals=function(a,b){return a.toLowerCase()==b.toLowerCase()};goog.string.subs=function(a,b){for(var c=a.split(\"%s\"),d=\"\",e=Array.prototype.slice.call(arguments,1);e.length&&1<c.length;)d+=c.shift()+e.shift();return d+c.join(\"%s\")};goog.string.collapseWhitespace=function(a){return a.replace(/[\\s\\xa0]+/g,\" \").replace(/^\\s+|\\s+$/g,\"\")};goog.string.isEmpty=function(a){return/^[\\s\\xa0]*$/.test(a)};goog.string.isEmptySafe=function(a){return goog.string.isEmpty(goog.string.makeSafe(a))};\ngoog.string.isBreakingWhitespace=function(a){return!/[^\\t\\n\\r ]/.test(a)};goog.string.isAlpha=function(a){return!/[^a-zA-Z]/.test(a)};goog.string.isNumeric=function(a){return!/[^0-9]/.test(a)};goog.string.isAlphaNumeric=function(a){return!/[^a-zA-Z0-9]/.test(a)};goog.string.isSpace=function(a){return\" \"==a};goog.string.isUnicodeChar=function(a){return 1==a.length&&\" \"<=a&&\"~\">=a||\"\\u0080\"<=a&&\"\\ufffd\">=a};goog.string.stripNewlines=function(a){return a.replace(/(\\r\\n|\\r|\\n)+/g,\" \")};\ngoog.string.canonicalizeNewlines=function(a){return a.replace(/(\\r\\n|\\r|\\n)/g,\"\\n\")};goog.string.normalizeWhitespace=function(a){return a.replace(/\\xa0|\\s/g,\" \")};goog.string.normalizeSpaces=function(a){return a.replace(/\\xa0|[ \\t]+/g,\" \")};goog.string.collapseBreakingSpaces=function(a){return a.replace(/[\\t\\r\\n ]+/g,\" \").replace(/^[\\t\\r\\n ]+|[\\t\\r\\n ]+$/g,\"\")};goog.string.trim=function(a){return a.replace(/^[\\s\\xa0]+|[\\s\\xa0]+$/g,\"\")};\ngoog.string.trimLeft=function(a){return a.replace(/^[\\s\\xa0]+/,\"\")};goog.string.trimRight=function(a){return a.replace(/[\\s\\xa0]+$/,\"\")};goog.string.caseInsensitiveCompare=function(a,b){var c=String(a).toLowerCase(),d=String(b).toLowerCase();return c<d?-1:c==d?0:1};goog.string.numerateCompareRegExp_=/(\\.\\d+)|(\\d+)|(\\D+)/g;\ngoog.string.numerateCompare=function(a,b){if(a==b)return 0;if(!a)return-1;if(!b)return 1;for(var c=a.toLowerCase().match(goog.string.numerateCompareRegExp_),d=b.toLowerCase().match(goog.string.numerateCompareRegExp_),e=Math.min(c.length,d.length),f=0;f<e;f++){var g=c[f],h=d[f];if(g!=h)return c=parseInt(g,10),!isNaN(c)&&(d=parseInt(h,10),!isNaN(d)&&c-d)?c-d:g<h?-1:1}return c.length!=d.length?c.length-d.length:a<b?-1:1};goog.string.urlEncode=function(a){return encodeURIComponent(String(a))};\ngoog.string.urlDecode=function(a){return decodeURIComponent(a.replace(/\\+/g,\" \"))};goog.string.newLineToBr=function(a,b){return a.replace(/(\\r\\n|\\r|\\n)/g,b?\"<br />\":\"<br>\")};\ngoog.string.htmlEscape=function(a,b){if(b)return a.replace(goog.string.amperRe_,\"&amp;\").replace(goog.string.ltRe_,\"&lt;\").replace(goog.string.gtRe_,\"&gt;\").replace(goog.string.quotRe_,\"&quot;\");if(!goog.string.allRe_.test(a))return a;-1!=a.indexOf(\"&\")&&(a=a.replace(goog.string.amperRe_,\"&amp;\"));-1!=a.indexOf(\"<\")&&(a=a.replace(goog.string.ltRe_,\"&lt;\"));-1!=a.indexOf(\">\")&&(a=a.replace(goog.string.gtRe_,\"&gt;\"));-1!=a.indexOf('\"')&&(a=a.replace(goog.string.quotRe_,\"&quot;\"));return a};\ngoog.string.amperRe_=/&/g;goog.string.ltRe_=/</g;goog.string.gtRe_=/>/g;goog.string.quotRe_=/\\\"/g;goog.string.allRe_=/[&<>\\\"]/;goog.string.unescapeEntities=function(a){return goog.string.contains(a,\"&\")?\"document\"in goog.global?goog.string.unescapeEntitiesUsingDom_(a):goog.string.unescapePureXmlEntities_(a):a};\ngoog.string.unescapeEntitiesUsingDom_=function(a){var b={\"&amp;\":\"&\",\"&lt;\":\"<\",\"&gt;\":\">\",\"&quot;\":'\"'},c=document.createElement(\"div\");return a.replace(goog.string.HTML_ENTITY_PATTERN_,function(a,e){var f=b[a];if(f)return f;if(\"#\"==e.charAt(0)){var g=Number(\"0\"+e.substr(1));isNaN(g)||(f=String.fromCharCode(g))}f||(c.innerHTML=a+\" \",f=c.firstChild.nodeValue.slice(0,-1));return b[a]=f})};\ngoog.string.unescapePureXmlEntities_=function(a){return a.replace(/&([^;]+);/g,function(a,c){switch(c){case \"amp\":return\"&\";case \"lt\":return\"<\";case \"gt\":return\">\";case \"quot\":return'\"';default:if(\"#\"==c.charAt(0)){var d=Number(\"0\"+c.substr(1));if(!isNaN(d))return String.fromCharCode(d)}return a}})};goog.string.HTML_ENTITY_PATTERN_=/&([^;\\s<&]+);?/g;goog.string.whitespaceEscape=function(a,b){return goog.string.newLineToBr(a.replace(/  /g,\" &#160;\"),b)};\ngoog.string.stripQuotes=function(a,b){for(var c=b.length,d=0;d<c;d++){var e=1==c?b:b.charAt(d);if(a.charAt(0)==e&&a.charAt(a.length-1)==e)return a.substring(1,a.length-1)}return a};goog.string.truncate=function(a,b,c){c&&(a=goog.string.unescapeEntities(a));a.length>b&&(a=a.substring(0,b-3)+\"...\");c&&(a=goog.string.htmlEscape(a));return a};\ngoog.string.truncateMiddle=function(a,b,c,d){c&&(a=goog.string.unescapeEntities(a));if(d&&a.length>b){d>b&&(d=b);var e=a.length-d;a=a.substring(0,b-d)+\"...\"+a.substring(e)}else a.length>b&&(d=Math.floor(b/2),e=a.length-d,a=a.substring(0,d+b%2)+\"...\"+a.substring(e));c&&(a=goog.string.htmlEscape(a));return a};goog.string.specialEscapeChars_={\"\\x00\":\"\\\\0\",\"\\b\":\"\\\\b\",\"\\f\":\"\\\\f\",\"\\n\":\"\\\\n\",\"\\r\":\"\\\\r\",\"\\t\":\"\\\\t\",\"\\x0B\":\"\\\\x0B\",'\"':'\\\\\"',\"\\\\\":\"\\\\\\\\\"};goog.string.jsEscapeCache_={\"'\":\"\\\\'\"};\ngoog.string.quote=function(a){a=String(a);if(a.quote)return a.quote();for(var b=['\"'],c=0;c<a.length;c++){var d=a.charAt(c),e=d.charCodeAt(0);b[c+1]=goog.string.specialEscapeChars_[d]||(31<e&&127>e?d:goog.string.escapeChar(d))}b.push('\"');return b.join(\"\")};goog.string.escapeString=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=goog.string.escapeChar(a.charAt(c));return b.join(\"\")};\ngoog.string.escapeChar=function(a){if(a in goog.string.jsEscapeCache_)return goog.string.jsEscapeCache_[a];if(a in goog.string.specialEscapeChars_)return goog.string.jsEscapeCache_[a]=goog.string.specialEscapeChars_[a];var b=a,c=a.charCodeAt(0);if(31<c&&127>c)b=a;else{if(256>c){if(b=\"\\\\x\",16>c||256<c)b+=\"0\"}else b=\"\\\\u\",4096>c&&(b+=\"0\");b+=c.toString(16).toUpperCase()}return goog.string.jsEscapeCache_[a]=b};goog.string.toMap=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=!0;return b};\ngoog.string.contains=function(a,b){return-1!=a.indexOf(b)};goog.string.countOf=function(a,b){return a&&b?a.split(b).length-1:0};goog.string.removeAt=function(a,b,c){var d=a;0<=b&&b<a.length&&0<c&&(d=a.substr(0,b)+a.substr(b+c,a.length-b-c));return d};goog.string.remove=function(a,b){var c=RegExp(goog.string.regExpEscape(b),\"\");return a.replace(c,\"\")};goog.string.removeAll=function(a,b){var c=RegExp(goog.string.regExpEscape(b),\"g\");return a.replace(c,\"\")};\ngoog.string.regExpEscape=function(a){return String(a).replace(/([-()\\[\\]{}+?*.$\\^|,:#<!\\\\])/g,\"\\\\$1\").replace(/\\x08/g,\"\\\\x08\")};goog.string.repeat=function(a,b){return Array(b+1).join(a)};goog.string.padNumber=function(a,b,c){a=goog.isDef(c)?a.toFixed(c):String(a);c=a.indexOf(\".\");-1==c&&(c=a.length);return goog.string.repeat(\"0\",Math.max(0,b-c))+a};goog.string.makeSafe=function(a){return null==a?\"\":String(a)};goog.string.buildString=function(a){return Array.prototype.join.call(arguments,\"\")};\ngoog.string.getRandomString=function(){return Math.floor(2147483648*Math.random()).toString(36)+Math.abs(Math.floor(2147483648*Math.random())^goog.now()).toString(36)};\ngoog.string.compareVersions=function(a,b){for(var c=0,d=goog.string.trim(String(a)).split(\".\"),e=goog.string.trim(String(b)).split(\".\"),f=Math.max(d.length,e.length),g=0;0==c&&g<f;g++){var h=d[g]||\"\",k=e[g]||\"\",l=RegExp(\"(\\\\d*)(\\\\D*)\",\"g\"),p=RegExp(\"(\\\\d*)(\\\\D*)\",\"g\");do{var m=l.exec(h)||[\"\",\"\",\"\"],n=p.exec(k)||[\"\",\"\",\"\"];if(0==m[0].length&&0==n[0].length)break;var c=0==m[1].length?0:parseInt(m[1],10),q=0==n[1].length?0:parseInt(n[1],10),c=goog.string.compareElements_(c,q)||goog.string.compareElements_(0==\nm[2].length,0==n[2].length)||goog.string.compareElements_(m[2],n[2])}while(0==c)}return c};goog.string.compareElements_=function(a,b){return a<b?-1:a>b?1:0};goog.string.HASHCODE_MAX_=4294967296;goog.string.hashCode=function(a){for(var b=0,c=0;c<a.length;++c)b=31*b+a.charCodeAt(c),b%=goog.string.HASHCODE_MAX_;return b};goog.string.uniqueStringCounter_=2147483648*Math.random()|0;goog.string.createUniqueString=function(){return\"goog_\"+goog.string.uniqueStringCounter_++};\ngoog.string.toNumber=function(a){var b=Number(a);return 0==b&&goog.string.isEmpty(a)?NaN:b};goog.string.isLowerCamelCase=function(a){return/^[a-z]+([A-Z][a-z]*)*$/.test(a)};goog.string.isUpperCamelCase=function(a){return/^([A-Z][a-z]*)+$/.test(a)};goog.string.toCamelCase=function(a){return String(a).replace(/\\-([a-z])/g,function(a,c){return c.toUpperCase()})};goog.string.toSelectorCase=function(a){return String(a).replace(/([A-Z])/g,\"-$1\").toLowerCase()};\ngoog.string.toTitleCase=function(a,b){var c=goog.isString(b)?goog.string.regExpEscape(b):\"\\\\s\";return a.replace(RegExp(\"(^\"+(c?\"|[\"+c+\"]+\":\"\")+\")([a-z])\",\"g\"),function(a,b,c){return b+c.toUpperCase()})};goog.string.parseInt=function(a){isFinite(a)&&(a=String(a));return goog.isString(a)?/^\\s*-?0x/i.test(a)?parseInt(a,16):parseInt(a,10):NaN};goog.string.splitLimit=function(a,b,c){a=a.split(b);for(var d=[];0<c&&a.length;)d.push(a.shift()),c--;a.length&&d.push(a.join(b));return d};goog.asserts={};goog.asserts.ENABLE_ASSERTS=goog.DEBUG;goog.asserts.AssertionError=function(a,b){b.unshift(a);goog.debug.Error.call(this,goog.string.subs.apply(null,b));b.shift();this.messagePattern=a};goog.inherits(goog.asserts.AssertionError,goog.debug.Error);goog.asserts.AssertionError.prototype.name=\"AssertionError\";goog.asserts.doAssertFailure_=function(a,b,c,d){var e=\"Assertion failed\";if(c)var e=e+(\": \"+c),f=d;else a&&(e+=\": \"+a,f=b);throw new goog.asserts.AssertionError(\"\"+e,f||[]);};\ngoog.asserts.assert=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!a&&goog.asserts.doAssertFailure_(\"\",null,b,Array.prototype.slice.call(arguments,2));return a};goog.asserts.fail=function(a,b){if(goog.asserts.ENABLE_ASSERTS)throw new goog.asserts.AssertionError(\"Failure\"+(a?\": \"+a:\"\"),Array.prototype.slice.call(arguments,1));};\ngoog.asserts.assertNumber=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!goog.isNumber(a)&&goog.asserts.doAssertFailure_(\"Expected number but got %s: %s.\",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};goog.asserts.assertString=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!goog.isString(a)&&goog.asserts.doAssertFailure_(\"Expected string but got %s: %s.\",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};\ngoog.asserts.assertFunction=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!goog.isFunction(a)&&goog.asserts.doAssertFailure_(\"Expected function but got %s: %s.\",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};goog.asserts.assertObject=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!goog.isObject(a)&&goog.asserts.doAssertFailure_(\"Expected object but got %s: %s.\",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};\ngoog.asserts.assertArray=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!goog.isArray(a)&&goog.asserts.doAssertFailure_(\"Expected array but got %s: %s.\",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};goog.asserts.assertBoolean=function(a,b,c){goog.asserts.ENABLE_ASSERTS&&!goog.isBoolean(a)&&goog.asserts.doAssertFailure_(\"Expected boolean but got %s: %s.\",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};\ngoog.asserts.assertElement=function(a,b,c){!goog.asserts.ENABLE_ASSERTS||goog.isObject(a)&&a.nodeType==goog.dom.NodeType.ELEMENT||goog.asserts.doAssertFailure_(\"Expected Element but got %s: %s.\",[goog.typeOf(a),a],b,Array.prototype.slice.call(arguments,2));return a};goog.asserts.assertInstanceof=function(a,b,c,d){!goog.asserts.ENABLE_ASSERTS||a instanceof b||goog.asserts.doAssertFailure_(\"instanceof check failed.\",null,c,Array.prototype.slice.call(arguments,3));return a};\ngoog.asserts.assertObjectPrototypeIsIntact=function(){for(var a in Object.prototype)goog.asserts.fail(a+\" should not be enumerable in Object.prototype.\")};goog.array={};goog.NATIVE_ARRAY_PROTOTYPES=goog.TRUSTED_SITE;goog.array.peek=function(a){return a[a.length-1]};goog.array.ARRAY_PROTOTYPE_=Array.prototype;\ngoog.array.indexOf=goog.NATIVE_ARRAY_PROTOTYPES&&goog.array.ARRAY_PROTOTYPE_.indexOf?function(a,b,c){goog.asserts.assert(null!=a.length);return goog.array.ARRAY_PROTOTYPE_.indexOf.call(a,b,c)}:function(a,b,c){c=null==c?0:0>c?Math.max(0,a.length+c):c;if(goog.isString(a))return goog.isString(b)&&1==b.length?a.indexOf(b,c):-1;for(;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1};\ngoog.array.lastIndexOf=goog.NATIVE_ARRAY_PROTOTYPES&&goog.array.ARRAY_PROTOTYPE_.lastIndexOf?function(a,b,c){goog.asserts.assert(null!=a.length);return goog.array.ARRAY_PROTOTYPE_.lastIndexOf.call(a,b,null==c?a.length-1:c)}:function(a,b,c){c=null==c?a.length-1:c;0>c&&(c=Math.max(0,a.length+c));if(goog.isString(a))return goog.isString(b)&&1==b.length?a.lastIndexOf(b,c):-1;for(;0<=c;c--)if(c in a&&a[c]===b)return c;return-1};\ngoog.array.forEach=goog.NATIVE_ARRAY_PROTOTYPES&&goog.array.ARRAY_PROTOTYPE_.forEach?function(a,b,c){goog.asserts.assert(null!=a.length);goog.array.ARRAY_PROTOTYPE_.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=goog.isString(a)?a.split(\"\"):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)};goog.array.forEachRight=function(a,b,c){for(var d=a.length,e=goog.isString(a)?a.split(\"\"):a,d=d-1;0<=d;--d)d in e&&b.call(c,e[d],d,a)};\ngoog.array.filter=goog.NATIVE_ARRAY_PROTOTYPES&&goog.array.ARRAY_PROTOTYPE_.filter?function(a,b,c){goog.asserts.assert(null!=a.length);return goog.array.ARRAY_PROTOTYPE_.filter.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=[],f=0,g=goog.isString(a)?a.split(\"\"):a,h=0;h<d;h++)if(h in g){var k=g[h];b.call(c,k,h,a)&&(e[f++]=k)}return e};\ngoog.array.map=goog.NATIVE_ARRAY_PROTOTYPES&&goog.array.ARRAY_PROTOTYPE_.map?function(a,b,c){goog.asserts.assert(null!=a.length);return goog.array.ARRAY_PROTOTYPE_.map.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=Array(d),f=goog.isString(a)?a.split(\"\"):a,g=0;g<d;g++)g in f&&(e[g]=b.call(c,f[g],g,a));return e};\ngoog.array.reduce=goog.NATIVE_ARRAY_PROTOTYPES&&goog.array.ARRAY_PROTOTYPE_.reduce?function(a,b,c,d){goog.asserts.assert(null!=a.length);d&&(b=goog.bind(b,d));return goog.array.ARRAY_PROTOTYPE_.reduce.call(a,b,c)}:function(a,b,c,d){var e=c;goog.array.forEach(a,function(c,g){e=b.call(d,e,c,g,a)});return e};\ngoog.array.reduceRight=goog.NATIVE_ARRAY_PROTOTYPES&&goog.array.ARRAY_PROTOTYPE_.reduceRight?function(a,b,c,d){goog.asserts.assert(null!=a.length);d&&(b=goog.bind(b,d));return goog.array.ARRAY_PROTOTYPE_.reduceRight.call(a,b,c)}:function(a,b,c,d){var e=c;goog.array.forEachRight(a,function(c,g){e=b.call(d,e,c,g,a)});return e};\ngoog.array.some=goog.NATIVE_ARRAY_PROTOTYPES&&goog.array.ARRAY_PROTOTYPE_.some?function(a,b,c){goog.asserts.assert(null!=a.length);return goog.array.ARRAY_PROTOTYPE_.some.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=goog.isString(a)?a.split(\"\"):a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return!0;return!1};\ngoog.array.every=goog.NATIVE_ARRAY_PROTOTYPES&&goog.array.ARRAY_PROTOTYPE_.every?function(a,b,c){goog.asserts.assert(null!=a.length);return goog.array.ARRAY_PROTOTYPE_.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=goog.isString(a)?a.split(\"\"):a,f=0;f<d;f++)if(f in e&&!b.call(c,e[f],f,a))return!1;return!0};goog.array.count=function(a,b,c){var d=0;goog.array.forEach(a,function(a,f,g){b.call(c,a,f,g)&&++d},c);return d};\ngoog.array.find=function(a,b,c){b=goog.array.findIndex(a,b,c);return 0>b?null:goog.isString(a)?a.charAt(b):a[b]};goog.array.findIndex=function(a,b,c){for(var d=a.length,e=goog.isString(a)?a.split(\"\"):a,f=0;f<d;f++)if(f in e&&b.call(c,e[f],f,a))return f;return-1};goog.array.findRight=function(a,b,c){b=goog.array.findIndexRight(a,b,c);return 0>b?null:goog.isString(a)?a.charAt(b):a[b]};\ngoog.array.findIndexRight=function(a,b,c){for(var d=a.length,e=goog.isString(a)?a.split(\"\"):a,d=d-1;0<=d;d--)if(d in e&&b.call(c,e[d],d,a))return d;return-1};goog.array.contains=function(a,b){return 0<=goog.array.indexOf(a,b)};goog.array.isEmpty=function(a){return 0==a.length};goog.array.clear=function(a){if(!goog.isArray(a))for(var b=a.length-1;0<=b;b--)delete a[b];a.length=0};goog.array.insert=function(a,b){goog.array.contains(a,b)||a.push(b)};\ngoog.array.insertAt=function(a,b,c){goog.array.splice(a,c,0,b)};goog.array.insertArrayAt=function(a,b,c){goog.partial(goog.array.splice,a,c,0).apply(null,b)};goog.array.insertBefore=function(a,b,c){var d;2==arguments.length||0>(d=goog.array.indexOf(a,c))?a.push(b):goog.array.insertAt(a,b,d)};goog.array.remove=function(a,b){var c=goog.array.indexOf(a,b),d;(d=0<=c)&&goog.array.removeAt(a,c);return d};\ngoog.array.removeAt=function(a,b){goog.asserts.assert(null!=a.length);return 1==goog.array.ARRAY_PROTOTYPE_.splice.call(a,b,1).length};goog.array.removeIf=function(a,b,c){b=goog.array.findIndex(a,b,c);return 0<=b?(goog.array.removeAt(a,b),!0):!1};goog.array.concat=function(a){return goog.array.ARRAY_PROTOTYPE_.concat.apply(goog.array.ARRAY_PROTOTYPE_,arguments)};goog.array.toArray=function(a){var b=a.length;if(0<b){for(var c=Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]};goog.array.clone=goog.array.toArray;\ngoog.array.extend=function(a,b){for(var c=1;c<arguments.length;c++){var d=arguments[c],e;if(goog.isArray(d)||(e=goog.isArrayLike(d))&&Object.prototype.hasOwnProperty.call(d,\"callee\"))a.push.apply(a,d);else if(e)for(var f=a.length,g=d.length,h=0;h<g;h++)a[f+h]=d[h];else a.push(d)}};goog.array.splice=function(a,b,c,d){goog.asserts.assert(null!=a.length);return goog.array.ARRAY_PROTOTYPE_.splice.apply(a,goog.array.slice(arguments,1))};\ngoog.array.slice=function(a,b,c){goog.asserts.assert(null!=a.length);return 2>=arguments.length?goog.array.ARRAY_PROTOTYPE_.slice.call(a,b):goog.array.ARRAY_PROTOTYPE_.slice.call(a,b,c)};goog.array.removeDuplicates=function(a,b,c){b=b||a;var d=function(a){return goog.isObject(g)?\"o\"+goog.getUid(g):(typeof g).charAt(0)+g};c=c||d;for(var d={},e=0,f=0;f<a.length;){var g=a[f++],h=c(g);Object.prototype.hasOwnProperty.call(d,h)||(d[h]=!0,b[e++]=g)}b.length=e};\ngoog.array.binarySearch=function(a,b,c){return goog.array.binarySearch_(a,c||goog.array.defaultCompare,!1,b)};goog.array.binarySelect=function(a,b,c){return goog.array.binarySearch_(a,b,!0,void 0,c)};goog.array.binarySearch_=function(a,b,c,d,e){for(var f=0,g=a.length,h;f<g;){var k=f+g>>1,l;l=c?b.call(e,a[k],k,a):b(d,a[k]);0<l?f=k+1:(g=k,h=!l)}return h?f:~f};goog.array.sort=function(a,b){goog.asserts.assert(null!=a.length);goog.array.ARRAY_PROTOTYPE_.sort.call(a,b||goog.array.defaultCompare)};\ngoog.array.stableSort=function(a,b){for(var c=0;c<a.length;c++)a[c]={index:c,value:a[c]};var d=b||goog.array.defaultCompare;goog.array.sort(a,function(a,b){return d(a.value,b.value)||a.index-b.index});for(c=0;c<a.length;c++)a[c]=a[c].value};goog.array.sortObjectsByKey=function(a,b,c){var d=c||goog.array.defaultCompare;goog.array.sort(a,function(a,c){return d(a[b],c[b])})};\ngoog.array.isSorted=function(a,b,c){b=b||goog.array.defaultCompare;for(var d=1;d<a.length;d++){var e=b(a[d-1],a[d]);if(0<e||0==e&&c)return!1}return!0};goog.array.equals=function(a,b,c){if(!goog.isArrayLike(a)||!goog.isArrayLike(b)||a.length!=b.length)return!1;var d=a.length;c=c||goog.array.defaultCompareEquality;for(var e=0;e<d;e++)if(!c(a[e],b[e]))return!1;return!0};goog.array.compare=function(a,b,c){return goog.array.equals(a,b,c)};\ngoog.array.compare3=function(a,b,c){c=c||goog.array.defaultCompare;for(var d=Math.min(a.length,b.length),e=0;e<d;e++){var f=c(a[e],b[e]);if(0!=f)return f}return goog.array.defaultCompare(a.length,b.length)};goog.array.defaultCompare=function(a,b){return a>b?1:a<b?-1:0};goog.array.defaultCompareEquality=function(a,b){return a===b};goog.array.binaryInsert=function(a,b,c){c=goog.array.binarySearch(a,b,c);return 0>c?(goog.array.insertAt(a,b,-(c+1)),!0):!1};\ngoog.array.binaryRemove=function(a,b,c){b=goog.array.binarySearch(a,b,c);return 0<=b?goog.array.removeAt(a,b):!1};goog.array.bucket=function(a,b,c){for(var d={},e=0;e<a.length;e++){var f=a[e],g=b.call(c,f,e,a);goog.isDef(g)&&(d[g]||(d[g]=[])).push(f)}return d};goog.array.toObject=function(a,b,c){var d={};goog.array.forEach(a,function(e,f){d[b.call(c,e,f,a)]=e});return d};\ngoog.array.range=function(a,b,c){var d=[],e=0,f=a;c=c||1;void 0!==b&&(e=a,f=b);if(0>c*(f-e))return[];if(0<c)for(a=e;a<f;a+=c)d.push(a);else for(a=e;a>f;a+=c)d.push(a);return d};goog.array.repeat=function(a,b){for(var c=[],d=0;d<b;d++)c[d]=a;return c};goog.array.flatten=function(a){for(var b=[],c=0;c<arguments.length;c++){var d=arguments[c];goog.isArray(d)?b.push.apply(b,goog.array.flatten.apply(null,d)):b.push(d)}return b};\ngoog.array.rotate=function(a,b){goog.asserts.assert(null!=a.length);a.length&&(b%=a.length,0<b?goog.array.ARRAY_PROTOTYPE_.unshift.apply(a,a.splice(-b,b)):0>b&&goog.array.ARRAY_PROTOTYPE_.push.apply(a,a.splice(0,-b)));return a};goog.array.moveItem=function(a,b,c){goog.asserts.assert(0<=b&&b<a.length);goog.asserts.assert(0<=c&&c<a.length);b=goog.array.ARRAY_PROTOTYPE_.splice.call(a,b,1);goog.array.ARRAY_PROTOTYPE_.splice.call(a,c,0,b[0])};\ngoog.array.zip=function(a){if(!arguments.length)return[];for(var b=[],c=0;;c++){for(var d=[],e=0;e<arguments.length;e++){var f=arguments[e];if(c>=f.length)return b;d.push(f[c])}b.push(d)}};goog.array.shuffle=function(a,b){for(var c=b||Math.random,d=a.length-1;0<d;d--){var e=Math.floor(c()*(d+1)),f=a[d];a[d]=a[e];a[e]=f}};goog.userAgent={};goog.userAgent.ASSUME_IE=!1;goog.userAgent.ASSUME_GECKO=!1;goog.userAgent.ASSUME_WEBKIT=!1;goog.userAgent.ASSUME_MOBILE_WEBKIT=!1;goog.userAgent.ASSUME_OPERA=!1;goog.userAgent.ASSUME_ANY_VERSION=!1;goog.userAgent.BROWSER_KNOWN_=goog.userAgent.ASSUME_IE||goog.userAgent.ASSUME_GECKO||goog.userAgent.ASSUME_MOBILE_WEBKIT||goog.userAgent.ASSUME_WEBKIT||goog.userAgent.ASSUME_OPERA;\ngoog.userAgent.getUserAgentString=function(){return goog.global.navigator?goog.global.navigator.userAgent:null};goog.userAgent.getNavigator=function(){return goog.global.navigator};\ngoog.userAgent.init_=function(){goog.userAgent.detectedOpera_=!1;goog.userAgent.detectedIe_=!1;goog.userAgent.detectedWebkit_=!1;goog.userAgent.detectedMobile_=!1;goog.userAgent.detectedGecko_=!1;var a;if(!goog.userAgent.BROWSER_KNOWN_&&(a=goog.userAgent.getUserAgentString())){var b=goog.userAgent.getNavigator();goog.userAgent.detectedOpera_=goog.string.startsWith(a,\"Opera\");goog.userAgent.detectedIe_=!goog.userAgent.detectedOpera_&&(goog.string.contains(a,\"MSIE\")||goog.string.contains(a,\"Trident\"));\ngoog.userAgent.detectedWebkit_=!goog.userAgent.detectedOpera_&&goog.string.contains(a,\"WebKit\");goog.userAgent.detectedMobile_=goog.userAgent.detectedWebkit_&&goog.string.contains(a,\"Mobile\");goog.userAgent.detectedGecko_=!goog.userAgent.detectedOpera_&&!goog.userAgent.detectedWebkit_&&!goog.userAgent.detectedIe_&&\"Gecko\"==b.product}};goog.userAgent.BROWSER_KNOWN_||goog.userAgent.init_();goog.userAgent.OPERA=goog.userAgent.BROWSER_KNOWN_?goog.userAgent.ASSUME_OPERA:goog.userAgent.detectedOpera_;\ngoog.userAgent.IE=goog.userAgent.BROWSER_KNOWN_?goog.userAgent.ASSUME_IE:goog.userAgent.detectedIe_;goog.userAgent.GECKO=goog.userAgent.BROWSER_KNOWN_?goog.userAgent.ASSUME_GECKO:goog.userAgent.detectedGecko_;goog.userAgent.WEBKIT=goog.userAgent.BROWSER_KNOWN_?goog.userAgent.ASSUME_WEBKIT||goog.userAgent.ASSUME_MOBILE_WEBKIT:goog.userAgent.detectedWebkit_;goog.userAgent.MOBILE=goog.userAgent.ASSUME_MOBILE_WEBKIT||goog.userAgent.detectedMobile_;goog.userAgent.SAFARI=goog.userAgent.WEBKIT;\ngoog.userAgent.determinePlatform_=function(){var a=goog.userAgent.getNavigator();return a&&a.platform||\"\"};goog.userAgent.PLATFORM=goog.userAgent.determinePlatform_();goog.userAgent.ASSUME_MAC=!1;goog.userAgent.ASSUME_WINDOWS=!1;goog.userAgent.ASSUME_LINUX=!1;goog.userAgent.ASSUME_X11=!1;goog.userAgent.ASSUME_ANDROID=!1;goog.userAgent.ASSUME_IPHONE=!1;goog.userAgent.ASSUME_IPAD=!1;\ngoog.userAgent.PLATFORM_KNOWN_=goog.userAgent.ASSUME_MAC||goog.userAgent.ASSUME_WINDOWS||goog.userAgent.ASSUME_LINUX||goog.userAgent.ASSUME_X11||goog.userAgent.ASSUME_ANDROID||goog.userAgent.ASSUME_IPHONE||goog.userAgent.ASSUME_IPAD;\ngoog.userAgent.initPlatform_=function(){goog.userAgent.detectedMac_=goog.string.contains(goog.userAgent.PLATFORM,\"Mac\");goog.userAgent.detectedWindows_=goog.string.contains(goog.userAgent.PLATFORM,\"Win\");goog.userAgent.detectedLinux_=goog.string.contains(goog.userAgent.PLATFORM,\"Linux\");goog.userAgent.detectedX11_=!!goog.userAgent.getNavigator()&&goog.string.contains(goog.userAgent.getNavigator().appVersion||\"\",\"X11\");var a=goog.userAgent.getUserAgentString();goog.userAgent.detectedAndroid_=!!a&&\ngoog.string.contains(a,\"Android\");goog.userAgent.detectedIPhone_=!!a&&goog.string.contains(a,\"iPhone\");goog.userAgent.detectedIPad_=!!a&&goog.string.contains(a,\"iPad\")};goog.userAgent.PLATFORM_KNOWN_||goog.userAgent.initPlatform_();goog.userAgent.MAC=goog.userAgent.PLATFORM_KNOWN_?goog.userAgent.ASSUME_MAC:goog.userAgent.detectedMac_;goog.userAgent.WINDOWS=goog.userAgent.PLATFORM_KNOWN_?goog.userAgent.ASSUME_WINDOWS:goog.userAgent.detectedWindows_;\ngoog.userAgent.LINUX=goog.userAgent.PLATFORM_KNOWN_?goog.userAgent.ASSUME_LINUX:goog.userAgent.detectedLinux_;goog.userAgent.X11=goog.userAgent.PLATFORM_KNOWN_?goog.userAgent.ASSUME_X11:goog.userAgent.detectedX11_;goog.userAgent.ANDROID=goog.userAgent.PLATFORM_KNOWN_?goog.userAgent.ASSUME_ANDROID:goog.userAgent.detectedAndroid_;goog.userAgent.IPHONE=goog.userAgent.PLATFORM_KNOWN_?goog.userAgent.ASSUME_IPHONE:goog.userAgent.detectedIPhone_;\ngoog.userAgent.IPAD=goog.userAgent.PLATFORM_KNOWN_?goog.userAgent.ASSUME_IPAD:goog.userAgent.detectedIPad_;\ngoog.userAgent.determineVersion_=function(){var a=\"\",b;goog.userAgent.OPERA&&goog.global.opera?(a=goog.global.opera.version,a=\"function\"==typeof a?a():a):(goog.userAgent.GECKO?b=/rv\\:([^\\);]+)(\\)|;)/:goog.userAgent.IE?b=/\\b(?:MSIE|rv)[: ]([^\\);]+)(\\)|;)/:goog.userAgent.WEBKIT&&(b=/WebKit\\/(\\S+)/),b&&(a=(a=b.exec(goog.userAgent.getUserAgentString()))?a[1]:\"\"));return goog.userAgent.IE&&(b=goog.userAgent.getDocumentMode_(),b>parseFloat(a))?String(b):a};\ngoog.userAgent.getDocumentMode_=function(){var a=goog.global.document;return a?a.documentMode:void 0};goog.userAgent.VERSION=goog.userAgent.determineVersion_();goog.userAgent.compare=function(a,b){return goog.string.compareVersions(a,b)};goog.userAgent.isVersionOrHigherCache_={};\ngoog.userAgent.isVersionOrHigher=function(a){return goog.userAgent.ASSUME_ANY_VERSION||goog.userAgent.isVersionOrHigherCache_[a]||(goog.userAgent.isVersionOrHigherCache_[a]=0<=goog.string.compareVersions(goog.userAgent.VERSION,a))};goog.userAgent.isVersion=goog.userAgent.isVersionOrHigher;goog.userAgent.isDocumentModeOrHigher=function(a){return goog.userAgent.IE&&goog.userAgent.DOCUMENT_MODE>=a};goog.userAgent.isDocumentMode=goog.userAgent.isDocumentModeOrHigher;\ngoog.userAgent.DOCUMENT_MODE=function(){var a=goog.global.document;return a&&goog.userAgent.IE?goog.userAgent.getDocumentMode_()||(\"CSS1Compat\"==a.compatMode?parseInt(goog.userAgent.VERSION,10):5):void 0}();goog.dom.BrowserFeature={CAN_ADD_NAME_OR_TYPE_ATTRIBUTES:!goog.userAgent.IE||goog.userAgent.isDocumentModeOrHigher(9),CAN_USE_CHILDREN_ATTRIBUTE:!goog.userAgent.GECKO&&!goog.userAgent.IE||goog.userAgent.IE&&goog.userAgent.isDocumentModeOrHigher(9)||goog.userAgent.GECKO&&goog.userAgent.isVersionOrHigher(\"1.9.1\"),CAN_USE_INNER_TEXT:goog.userAgent.IE&&!goog.userAgent.isVersionOrHigher(\"9\"),CAN_USE_PARENT_ELEMENT_PROPERTY:goog.userAgent.IE||goog.userAgent.OPERA||goog.userAgent.WEBKIT,INNER_HTML_NEEDS_SCOPED_ELEMENT:goog.userAgent.IE};goog.dom.TagName={A:\"A\",ABBR:\"ABBR\",ACRONYM:\"ACRONYM\",ADDRESS:\"ADDRESS\",APPLET:\"APPLET\",AREA:\"AREA\",ARTICLE:\"ARTICLE\",ASIDE:\"ASIDE\",AUDIO:\"AUDIO\",B:\"B\",BASE:\"BASE\",BASEFONT:\"BASEFONT\",BDI:\"BDI\",BDO:\"BDO\",BIG:\"BIG\",BLOCKQUOTE:\"BLOCKQUOTE\",BODY:\"BODY\",BR:\"BR\",BUTTON:\"BUTTON\",CANVAS:\"CANVAS\",CAPTION:\"CAPTION\",CENTER:\"CENTER\",CITE:\"CITE\",CODE:\"CODE\",COL:\"COL\",COLGROUP:\"COLGROUP\",COMMAND:\"COMMAND\",DATA:\"DATA\",DATALIST:\"DATALIST\",DD:\"DD\",DEL:\"DEL\",DETAILS:\"DETAILS\",DFN:\"DFN\",DIALOG:\"DIALOG\",DIR:\"DIR\",DIV:\"DIV\",\nDL:\"DL\",DT:\"DT\",EM:\"EM\",EMBED:\"EMBED\",FIELDSET:\"FIELDSET\",FIGCAPTION:\"FIGCAPTION\",FIGURE:\"FIGURE\",FONT:\"FONT\",FOOTER:\"FOOTER\",FORM:\"FORM\",FRAME:\"FRAME\",FRAMESET:\"FRAMESET\",H1:\"H1\",H2:\"H2\",H3:\"H3\",H4:\"H4\",H5:\"H5\",H6:\"H6\",HEAD:\"HEAD\",HEADER:\"HEADER\",HGROUP:\"HGROUP\",HR:\"HR\",HTML:\"HTML\",I:\"I\",IFRAME:\"IFRAME\",IMG:\"IMG\",INPUT:\"INPUT\",INS:\"INS\",ISINDEX:\"ISINDEX\",KBD:\"KBD\",KEYGEN:\"KEYGEN\",LABEL:\"LABEL\",LEGEND:\"LEGEND\",LI:\"LI\",LINK:\"LINK\",MAP:\"MAP\",MARK:\"MARK\",MATH:\"MATH\",MENU:\"MENU\",META:\"META\",METER:\"METER\",\nNAV:\"NAV\",NOFRAMES:\"NOFRAMES\",NOSCRIPT:\"NOSCRIPT\",OBJECT:\"OBJECT\",OL:\"OL\",OPTGROUP:\"OPTGROUP\",OPTION:\"OPTION\",OUTPUT:\"OUTPUT\",P:\"P\",PARAM:\"PARAM\",PRE:\"PRE\",PROGRESS:\"PROGRESS\",Q:\"Q\",RP:\"RP\",RT:\"RT\",RUBY:\"RUBY\",S:\"S\",SAMP:\"SAMP\",SCRIPT:\"SCRIPT\",SECTION:\"SECTION\",SELECT:\"SELECT\",SMALL:\"SMALL\",SOURCE:\"SOURCE\",SPAN:\"SPAN\",STRIKE:\"STRIKE\",STRONG:\"STRONG\",STYLE:\"STYLE\",SUB:\"SUB\",SUMMARY:\"SUMMARY\",SUP:\"SUP\",SVG:\"SVG\",TABLE:\"TABLE\",TBODY:\"TBODY\",TD:\"TD\",TEXTAREA:\"TEXTAREA\",TFOOT:\"TFOOT\",TH:\"TH\",THEAD:\"THEAD\",\nTIME:\"TIME\",TITLE:\"TITLE\",TR:\"TR\",TRACK:\"TRACK\",TT:\"TT\",U:\"U\",UL:\"UL\",VAR:\"VAR\",VIDEO:\"VIDEO\",WBR:\"WBR\"};goog.dom.classes={};goog.dom.classes.set=function(a,b){a.className=b};goog.dom.classes.get=function(a){a=a.className;return goog.isString(a)&&a.match(/\\S+/g)||[]};goog.dom.classes.add=function(a,b){var c=goog.dom.classes.get(a),d=goog.array.slice(arguments,1),e=c.length+d.length;goog.dom.classes.add_(c,d);goog.dom.classes.set(a,c.join(\" \"));return c.length==e};\ngoog.dom.classes.remove=function(a,b){var c=goog.dom.classes.get(a),d=goog.array.slice(arguments,1),e=goog.dom.classes.getDifference_(c,d);goog.dom.classes.set(a,e.join(\" \"));return e.length==c.length-d.length};goog.dom.classes.add_=function(a,b){for(var c=0;c<b.length;c++)goog.array.contains(a,b[c])||a.push(b[c])};goog.dom.classes.getDifference_=function(a,b){return goog.array.filter(a,function(a){return!goog.array.contains(b,a)})};\ngoog.dom.classes.swap=function(a,b,c){for(var d=goog.dom.classes.get(a),e=!1,f=0;f<d.length;f++)d[f]==b&&(goog.array.splice(d,f--,1),e=!0);e&&(d.push(c),goog.dom.classes.set(a,d.join(\" \")));return e};goog.dom.classes.addRemove=function(a,b,c){var d=goog.dom.classes.get(a);goog.isString(b)?goog.array.remove(d,b):goog.isArray(b)&&(d=goog.dom.classes.getDifference_(d,b));goog.isString(c)&&!goog.array.contains(d,c)?d.push(c):goog.isArray(c)&&goog.dom.classes.add_(d,c);goog.dom.classes.set(a,d.join(\" \"))};\ngoog.dom.classes.has=function(a,b){return goog.array.contains(goog.dom.classes.get(a),b)};goog.dom.classes.enable=function(a,b,c){c?goog.dom.classes.add(a,b):goog.dom.classes.remove(a,b)};goog.dom.classes.toggle=function(a,b){var c=!goog.dom.classes.has(a,b);goog.dom.classes.enable(a,b,c);return c};goog.functions={};goog.functions.constant=function(a){return function(){return a}};goog.functions.FALSE=goog.functions.constant(!1);goog.functions.TRUE=goog.functions.constant(!0);goog.functions.NULL=goog.functions.constant(null);goog.functions.identity=function(a,b){return a};goog.functions.error=function(a){return function(){throw Error(a);}};goog.functions.fail=function(a){return function(){throw a;}};\ngoog.functions.lock=function(a,b){b=b||0;return function(){return a.apply(this,Array.prototype.slice.call(arguments,0,b))}};goog.functions.nth=function(a){return function(){return arguments[a]}};goog.functions.withReturnValue=function(a,b){return goog.functions.sequence(a,goog.functions.constant(b))};goog.functions.compose=function(a,b){var c=arguments,d=c.length;return function(){var a;d&&(a=c[d-1].apply(this,arguments));for(var b=d-2;0<=b;b--)a=c[b].call(this,a);return a}};\ngoog.functions.sequence=function(a){var b=arguments,c=b.length;return function(){for(var a,e=0;e<c;e++)a=b[e].apply(this,arguments);return a}};goog.functions.and=function(a){var b=arguments,c=b.length;return function(){for(var a=0;a<c;a++)if(!b[a].apply(this,arguments))return!1;return!0}};goog.functions.or=function(a){var b=arguments,c=b.length;return function(){for(var a=0;a<c;a++)if(b[a].apply(this,arguments))return!0;return!1}};\ngoog.functions.not=function(a){return function(){return!a.apply(this,arguments)}};goog.functions.create=function(a,b){var c=function(){};c.prototype=a.prototype;c=new c;a.apply(c,Array.prototype.slice.call(arguments,1));return c};goog.functions.CACHE_RETURN_VALUE=!0;goog.functions.cacheReturnValue=function(a){var b=!1,c;return function(){if(!goog.functions.CACHE_RETURN_VALUE)return a();b||(c=a(),b=!0);return c}};goog.math={};goog.math.randomInt=function(a){return Math.floor(Math.random()*a)};goog.math.uniformRandom=function(a,b){return a+Math.random()*(b-a)};goog.math.clamp=function(a,b,c){return Math.min(Math.max(a,b),c)};goog.math.modulo=function(a,b){var c=a%b;return 0>c*b?c+b:c};goog.math.lerp=function(a,b,c){return a+c*(b-a)};goog.math.nearlyEquals=function(a,b,c){return Math.abs(a-b)<=(c||1E-6)};goog.math.standardAngle=function(a){return goog.math.modulo(a,360)};\ngoog.math.toRadians=function(a){return a*Math.PI/180};goog.math.toDegrees=function(a){return 180*a/Math.PI};goog.math.angleDx=function(a,b){return b*Math.cos(goog.math.toRadians(a))};goog.math.angleDy=function(a,b){return b*Math.sin(goog.math.toRadians(a))};goog.math.angle=function(a,b,c,d){return goog.math.standardAngle(goog.math.toDegrees(Math.atan2(d-b,c-a)))};goog.math.angleDifference=function(a,b){var c=goog.math.standardAngle(b)-goog.math.standardAngle(a);180<c?c-=360:-180>=c&&(c=360+c);return c};\ngoog.math.sign=function(a){return 0==a?0:0>a?-1:1};goog.math.longestCommonSubsequence=function(a,b,c,d){c=c||function(a,b){return a==b};d=d||function(b,c){return a[b]};for(var e=a.length,f=b.length,g=[],h=0;h<e+1;h++)g[h]=[],g[h][0]=0;for(var k=0;k<f+1;k++)g[0][k]=0;for(h=1;h<=e;h++)for(k=1;k<=f;k++)c(a[h-1],b[k-1])?g[h][k]=g[h-1][k-1]+1:g[h][k]=Math.max(g[h-1][k],g[h][k-1]);for(var l=[],h=e,k=f;0<h&&0<k;)c(a[h-1],b[k-1])?(l.unshift(d(h-1,k-1)),h--,k--):g[h-1][k]>g[h][k-1]?h--:k--;return l};\ngoog.math.sum=function(a){return goog.array.reduce(arguments,function(a,c){return a+c},0)};goog.math.average=function(a){return goog.math.sum.apply(null,arguments)/arguments.length};goog.math.sampleVariance=function(a){var b=arguments.length;if(2>b)return 0;var c=goog.math.average.apply(null,arguments);return goog.math.sum.apply(null,goog.array.map(arguments,function(a){return Math.pow(a-c,2)}))/(b-1)};goog.math.standardDeviation=function(a){return Math.sqrt(goog.math.sampleVariance.apply(null,arguments))};\ngoog.math.isInt=function(a){return isFinite(a)&&0==a%1};goog.math.isFiniteNumber=function(a){return isFinite(a)&&!isNaN(a)};goog.math.safeFloor=function(a,b){goog.asserts.assert(!goog.isDef(b)||0<b);return Math.floor(a+(b||2E-15))};goog.math.safeCeil=function(a,b){goog.asserts.assert(!goog.isDef(b)||0<b);return Math.ceil(a-(b||2E-15))};goog.math.Coordinate=function(a,b){this.x=goog.isDef(a)?a:0;this.y=goog.isDef(b)?b:0};goog.math.Coordinate.prototype.clone=function(){return new goog.math.Coordinate(this.x,this.y)};goog.DEBUG&&(goog.math.Coordinate.prototype.toString=function(){return\"(\"+this.x+\", \"+this.y+\")\"});goog.math.Coordinate.equals=function(a,b){return a==b?!0:a&&b?a.x==b.x&&a.y==b.y:!1};goog.math.Coordinate.distance=function(a,b){var c=a.x-b.x,d=a.y-b.y;return Math.sqrt(c*c+d*d)};\ngoog.math.Coordinate.magnitude=function(a){return Math.sqrt(a.x*a.x+a.y*a.y)};goog.math.Coordinate.azimuth=function(a){return goog.math.angle(0,0,a.x,a.y)};goog.math.Coordinate.squaredDistance=function(a,b){var c=a.x-b.x,d=a.y-b.y;return c*c+d*d};goog.math.Coordinate.difference=function(a,b){return new goog.math.Coordinate(a.x-b.x,a.y-b.y)};goog.math.Coordinate.sum=function(a,b){return new goog.math.Coordinate(a.x+b.x,a.y+b.y)};\ngoog.math.Coordinate.prototype.ceil=function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this};goog.math.Coordinate.prototype.floor=function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this};goog.math.Coordinate.prototype.round=function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this};goog.math.Coordinate.prototype.translate=function(a,b){a instanceof goog.math.Coordinate?(this.x+=a.x,this.y+=a.y):(this.x+=a,goog.isNumber(b)&&(this.y+=b));return this};\ngoog.math.Coordinate.prototype.scale=function(a,b){var c=goog.isNumber(b)?b:a;this.x*=a;this.y*=c;return this};goog.math.Size=function(a,b){this.width=a;this.height=b};goog.math.Size.equals=function(a,b){return a==b?!0:a&&b?a.width==b.width&&a.height==b.height:!1};goog.math.Size.prototype.clone=function(){return new goog.math.Size(this.width,this.height)};goog.DEBUG&&(goog.math.Size.prototype.toString=function(){return\"(\"+this.width+\" x \"+this.height+\")\"});goog.math.Size.prototype.getLongest=function(){return Math.max(this.width,this.height)};\ngoog.math.Size.prototype.getShortest=function(){return Math.min(this.width,this.height)};goog.math.Size.prototype.area=function(){return this.width*this.height};goog.math.Size.prototype.perimeter=function(){return 2*(this.width+this.height)};goog.math.Size.prototype.aspectRatio=function(){return this.width/this.height};goog.math.Size.prototype.isEmpty=function(){return!this.area()};goog.math.Size.prototype.ceil=function(){this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};\ngoog.math.Size.prototype.fitsInside=function(a){return this.width<=a.width&&this.height<=a.height};goog.math.Size.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};goog.math.Size.prototype.round=function(){this.width=Math.round(this.width);this.height=Math.round(this.height);return this};goog.math.Size.prototype.scale=function(a,b){var c=goog.isNumber(b)?b:a;this.width*=a;this.height*=c;return this};\ngoog.math.Size.prototype.scaleToFit=function(a){a=this.aspectRatio()>a.aspectRatio()?a.width/this.width:a.height/this.height;return this.scale(a)};goog.object={};goog.object.forEach=function(a,b,c){for(var d in a)b.call(c,a[d],d,a)};goog.object.filter=function(a,b,c){var d={},e;for(e in a)b.call(c,a[e],e,a)&&(d[e]=a[e]);return d};goog.object.map=function(a,b,c){var d={},e;for(e in a)d[e]=b.call(c,a[e],e,a);return d};goog.object.some=function(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return!0;return!1};goog.object.every=function(a,b,c){for(var d in a)if(!b.call(c,a[d],d,a))return!1;return!0};\ngoog.object.getCount=function(a){var b=0,c;for(c in a)b++;return b};goog.object.getAnyKey=function(a){for(var b in a)return b};goog.object.getAnyValue=function(a){for(var b in a)return a[b]};goog.object.contains=function(a,b){return goog.object.containsValue(a,b)};goog.object.getValues=function(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b};goog.object.getKeys=function(a){var b=[],c=0,d;for(d in a)b[c++]=d;return b};\ngoog.object.getValueByKeys=function(a,b){for(var c=goog.isArrayLike(b),d=c?b:arguments,c=c?0:1;c<d.length&&(a=a[d[c]],goog.isDef(a));c++);return a};goog.object.containsKey=function(a,b){return b in a};goog.object.containsValue=function(a,b){for(var c in a)if(a[c]==b)return!0;return!1};goog.object.findKey=function(a,b,c){for(var d in a)if(b.call(c,a[d],d,a))return d};goog.object.findValue=function(a,b,c){return(b=goog.object.findKey(a,b,c))&&a[b]};\ngoog.object.isEmpty=function(a){for(var b in a)return!1;return!0};goog.object.clear=function(a){for(var b in a)delete a[b]};goog.object.remove=function(a,b){var c;(c=b in a)&&delete a[b];return c};goog.object.add=function(a,b,c){if(b in a)throw Error('The object already contains the key \"'+b+'\"');goog.object.set(a,b,c)};goog.object.get=function(a,b,c){return b in a?a[b]:c};goog.object.set=function(a,b,c){a[b]=c};goog.object.setIfUndefined=function(a,b,c){return b in a?a[b]:a[b]=c};\ngoog.object.clone=function(a){var b={},c;for(c in a)b[c]=a[c];return b};goog.object.unsafeClone=function(a){var b=goog.typeOf(a);if(\"object\"==b||\"array\"==b){if(a.clone)return a.clone();var b=\"array\"==b?[]:{},c;for(c in a)b[c]=goog.object.unsafeClone(a[c]);return b}return a};goog.object.transpose=function(a){var b={},c;for(c in a)b[a[c]]=c;return b};goog.object.PROTOTYPE_FIELDS_=\"constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf\".split(\" \");\ngoog.object.extend=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<goog.object.PROTOTYPE_FIELDS_.length;f++)c=goog.object.PROTOTYPE_FIELDS_[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};\ngoog.object.create=function(a){var b=arguments.length;if(1==b&&goog.isArray(arguments[0]))return goog.object.create.apply(null,arguments[0]);if(b%2)throw Error(\"Uneven number of arguments\");for(var c={},d=0;d<b;d+=2)c[arguments[d]]=arguments[d+1];return c};goog.object.createSet=function(a){var b=arguments.length;if(1==b&&goog.isArray(arguments[0]))return goog.object.createSet.apply(null,arguments[0]);for(var c={},d=0;d<b;d++)c[arguments[d]]=!0;return c};\ngoog.object.createImmutableView=function(a){var b=a;Object.isFrozen&&!Object.isFrozen(a)&&(b=Object.create(a),Object.freeze(b));return b};goog.object.isImmutableView=function(a){return!!Object.isFrozen&&Object.isFrozen(a)};goog.dom.ASSUME_QUIRKS_MODE=!1;goog.dom.ASSUME_STANDARDS_MODE=!1;goog.dom.COMPAT_MODE_KNOWN_=goog.dom.ASSUME_QUIRKS_MODE||goog.dom.ASSUME_STANDARDS_MODE;goog.dom.getDomHelper=function(a){return a?new goog.dom.DomHelper(goog.dom.getOwnerDocument(a)):goog.dom.defaultDomHelper_||(goog.dom.defaultDomHelper_=new goog.dom.DomHelper)};goog.dom.getDocument=function(){return document};goog.dom.getElement=function(a){return goog.dom.getElementHelper_(document,a)};\ngoog.dom.getElementHelper_=function(a,b){return goog.isString(b)?a.getElementById(b):b};goog.dom.getRequiredElement=function(a){return goog.dom.getRequiredElementHelper_(document,a)};goog.dom.getRequiredElementHelper_=function(a,b){goog.asserts.assertString(b);var c=goog.dom.getElementHelper_(a,b);return c=goog.asserts.assertElement(c,\"No element found with id: \"+b)};goog.dom.$=goog.dom.getElement;\ngoog.dom.getElementsByTagNameAndClass=function(a,b,c){return goog.dom.getElementsByTagNameAndClass_(document,a,b,c)};goog.dom.getElementsByClass=function(a,b){var c=b||document;return goog.dom.canUseQuerySelector_(c)?c.querySelectorAll(\".\"+a):c.getElementsByClassName?c.getElementsByClassName(a):goog.dom.getElementsByTagNameAndClass_(document,\"*\",a,b)};\ngoog.dom.getElementByClass=function(a,b){var c=b||document,d=null;return(d=goog.dom.canUseQuerySelector_(c)?c.querySelector(\".\"+a):goog.dom.getElementsByClass(a,b)[0])||null};goog.dom.canUseQuerySelector_=function(a){return!(!a.querySelectorAll||!a.querySelector)};\ngoog.dom.getElementsByTagNameAndClass_=function(a,b,c,d){a=d||a;b=b&&\"*\"!=b?b.toUpperCase():\"\";if(goog.dom.canUseQuerySelector_(a)&&(b||c))return a.querySelectorAll(b+(c?\".\"+c:\"\"));if(c&&a.getElementsByClassName){a=a.getElementsByClassName(c);if(b){d={};for(var e=0,f=0,g;g=a[f];f++)b==g.nodeName&&(d[e++]=g);d.length=e;return d}return a}a=a.getElementsByTagName(b||\"*\");if(c){d={};for(f=e=0;g=a[f];f++)b=g.className,\"function\"==typeof b.split&&goog.array.contains(b.split(/\\s+/),c)&&(d[e++]=g);d.length=\ne;return d}return a};goog.dom.$$=goog.dom.getElementsByTagNameAndClass;goog.dom.setProperties=function(a,b){goog.object.forEach(b,function(b,d){\"style\"==d?a.style.cssText=b:\"class\"==d?a.className=b:\"for\"==d?a.htmlFor=b:d in goog.dom.DIRECT_ATTRIBUTE_MAP_?a.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[d],b):goog.string.startsWith(d,\"aria-\")||goog.string.startsWith(d,\"data-\")?a.setAttribute(d,b):a[d]=b})};\ngoog.dom.DIRECT_ATTRIBUTE_MAP_={cellpadding:\"cellPadding\",cellspacing:\"cellSpacing\",colspan:\"colSpan\",frameborder:\"frameBorder\",height:\"height\",maxlength:\"maxLength\",role:\"role\",rowspan:\"rowSpan\",type:\"type\",usemap:\"useMap\",valign:\"vAlign\",width:\"width\"};goog.dom.getViewportSize=function(a){return goog.dom.getViewportSize_(a||window)};goog.dom.getViewportSize_=function(a){a=a.document;a=goog.dom.isCss1CompatMode_(a)?a.documentElement:a.body;return new goog.math.Size(a.clientWidth,a.clientHeight)};\ngoog.dom.getDocumentHeight=function(){return goog.dom.getDocumentHeight_(window)};goog.dom.getDocumentHeight_=function(a){var b=a.document,c=0;if(b){a=goog.dom.getViewportSize_(a).height;var c=b.body,d=b.documentElement;if(goog.dom.isCss1CompatMode_(b)&&d.scrollHeight)c=d.scrollHeight!=a?d.scrollHeight:d.offsetHeight;else{var b=d.scrollHeight,e=d.offsetHeight;d.clientHeight!=e&&(b=c.scrollHeight,e=c.offsetHeight);c=b>a?b>e?b:e:b<e?b:e}}return c};\ngoog.dom.getPageScroll=function(a){return goog.dom.getDomHelper((a||goog.global||window).document).getDocumentScroll()};goog.dom.getDocumentScroll=function(){return goog.dom.getDocumentScroll_(document)};\ngoog.dom.getDocumentScroll_=function(a){var b=goog.dom.getDocumentScrollElement_(a);a=goog.dom.getWindow_(a);return goog.userAgent.IE&&goog.userAgent.isVersionOrHigher(\"10\")&&a.pageYOffset!=b.scrollTop?new goog.math.Coordinate(b.scrollLeft,b.scrollTop):new goog.math.Coordinate(a.pageXOffset||b.scrollLeft,a.pageYOffset||b.scrollTop)};goog.dom.getDocumentScrollElement=function(){return goog.dom.getDocumentScrollElement_(document)};\ngoog.dom.getDocumentScrollElement_=function(a){return!goog.userAgent.WEBKIT&&goog.dom.isCss1CompatMode_(a)?a.documentElement:a.body||a.documentElement};goog.dom.getWindow=function(a){return a?goog.dom.getWindow_(a):window};goog.dom.getWindow_=function(a){return a.parentWindow||a.defaultView};goog.dom.createDom=function(a,b,c){return goog.dom.createDom_(document,arguments)};\ngoog.dom.createDom_=function(a,b){var c=b[0],d=b[1];if(!goog.dom.BrowserFeature.CAN_ADD_NAME_OR_TYPE_ATTRIBUTES&&d&&(d.name||d.type)){c=[\"<\",c];d.name&&c.push(' name=\"',goog.string.htmlEscape(d.name),'\"');if(d.type){c.push(' type=\"',goog.string.htmlEscape(d.type),'\"');var e={};goog.object.extend(e,d);delete e.type;d=e}c.push(\">\");c=c.join(\"\")}c=a.createElement(c);d&&(goog.isString(d)?c.className=d:goog.isArray(d)?goog.dom.classes.add.apply(null,[c].concat(d)):goog.dom.setProperties(c,d));2<b.length&&\ngoog.dom.append_(a,c,b,2);return c};goog.dom.append_=function(a,b,c,d){function e(c){c&&b.appendChild(goog.isString(c)?a.createTextNode(c):c)}for(;d<c.length;d++){var f=c[d];goog.isArrayLike(f)&&!goog.dom.isNodeLike(f)?goog.array.forEach(goog.dom.isNodeList(f)?goog.array.toArray(f):f,e):e(f)}};goog.dom.$dom=goog.dom.createDom;goog.dom.createElement=function(a){return document.createElement(a)};goog.dom.createTextNode=function(a){return document.createTextNode(String(a))};\ngoog.dom.createTable=function(a,b,c){return goog.dom.createTable_(document,a,b,!!c)};goog.dom.createTable_=function(a,b,c,d){for(var e=[\"<tr>\"],f=0;f<c;f++)e.push(d?\"<td>&nbsp;</td>\":\"<td></td>\");e.push(\"</tr>\");e=e.join(\"\");c=[\"<table>\"];for(f=0;f<b;f++)c.push(e);c.push(\"</table>\");a=a.createElement(goog.dom.TagName.DIV);a.innerHTML=c.join(\"\");return a.removeChild(a.firstChild)};goog.dom.htmlToDocumentFragment=function(a){return goog.dom.htmlToDocumentFragment_(document,a)};\ngoog.dom.htmlToDocumentFragment_=function(a,b){var c=a.createElement(\"div\");goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT?(c.innerHTML=\"<br>\"+b,c.removeChild(c.firstChild)):c.innerHTML=b;if(1==c.childNodes.length)return c.removeChild(c.firstChild);for(var d=a.createDocumentFragment();c.firstChild;)d.appendChild(c.firstChild);return d};goog.dom.isCss1CompatMode=function(){return goog.dom.isCss1CompatMode_(document)};\ngoog.dom.isCss1CompatMode_=function(a){return goog.dom.COMPAT_MODE_KNOWN_?goog.dom.ASSUME_STANDARDS_MODE:\"CSS1Compat\"==a.compatMode};goog.dom.canHaveChildren=function(a){if(a.nodeType!=goog.dom.NodeType.ELEMENT)return!1;switch(a.tagName){case goog.dom.TagName.APPLET:case goog.dom.TagName.AREA:case goog.dom.TagName.BASE:case goog.dom.TagName.BR:case goog.dom.TagName.COL:case goog.dom.TagName.COMMAND:case goog.dom.TagName.EMBED:case goog.dom.TagName.FRAME:case goog.dom.TagName.HR:case goog.dom.TagName.IMG:case goog.dom.TagName.INPUT:case goog.dom.TagName.IFRAME:case goog.dom.TagName.ISINDEX:case goog.dom.TagName.KEYGEN:case goog.dom.TagName.LINK:case goog.dom.TagName.NOFRAMES:case goog.dom.TagName.NOSCRIPT:case goog.dom.TagName.META:case goog.dom.TagName.OBJECT:case goog.dom.TagName.PARAM:case goog.dom.TagName.SCRIPT:case goog.dom.TagName.SOURCE:case goog.dom.TagName.STYLE:case goog.dom.TagName.TRACK:case goog.dom.TagName.WBR:return!1}return!0};\ngoog.dom.appendChild=function(a,b){a.appendChild(b)};goog.dom.append=function(a,b){goog.dom.append_(goog.dom.getOwnerDocument(a),a,arguments,1)};goog.dom.removeChildren=function(a){for(var b;b=a.firstChild;)a.removeChild(b)};goog.dom.insertSiblingBefore=function(a,b){b.parentNode&&b.parentNode.insertBefore(a,b)};goog.dom.insertSiblingAfter=function(a,b){b.parentNode&&b.parentNode.insertBefore(a,b.nextSibling)};goog.dom.insertChildAt=function(a,b,c){a.insertBefore(b,a.childNodes[c]||null)};\ngoog.dom.removeNode=function(a){return a&&a.parentNode?a.parentNode.removeChild(a):null};goog.dom.replaceNode=function(a,b){var c=b.parentNode;c&&c.replaceChild(a,b)};goog.dom.flattenElement=function(a){var b,c=a.parentNode;if(c&&c.nodeType!=goog.dom.NodeType.DOCUMENT_FRAGMENT){if(a.removeNode)return a.removeNode(!1);for(;b=a.firstChild;)c.insertBefore(b,a);return goog.dom.removeNode(a)}};\ngoog.dom.getChildren=function(a){return goog.dom.BrowserFeature.CAN_USE_CHILDREN_ATTRIBUTE&&void 0!=a.children?a.children:goog.array.filter(a.childNodes,function(a){return a.nodeType==goog.dom.NodeType.ELEMENT})};goog.dom.getFirstElementChild=function(a){return void 0!=a.firstElementChild?a.firstElementChild:goog.dom.getNextElementNode_(a.firstChild,!0)};goog.dom.getLastElementChild=function(a){return void 0!=a.lastElementChild?a.lastElementChild:goog.dom.getNextElementNode_(a.lastChild,!1)};\ngoog.dom.getNextElementSibling=function(a){return void 0!=a.nextElementSibling?a.nextElementSibling:goog.dom.getNextElementNode_(a.nextSibling,!0)};goog.dom.getPreviousElementSibling=function(a){return void 0!=a.previousElementSibling?a.previousElementSibling:goog.dom.getNextElementNode_(a.previousSibling,!1)};goog.dom.getNextElementNode_=function(a,b){for(;a&&a.nodeType!=goog.dom.NodeType.ELEMENT;)a=b?a.nextSibling:a.previousSibling;return a};\ngoog.dom.getNextNode=function(a){if(!a)return null;if(a.firstChild)return a.firstChild;for(;a&&!a.nextSibling;)a=a.parentNode;return a?a.nextSibling:null};goog.dom.getPreviousNode=function(a){if(!a)return null;if(!a.previousSibling)return a.parentNode;for(a=a.previousSibling;a&&a.lastChild;)a=a.lastChild;return a};goog.dom.isNodeLike=function(a){return goog.isObject(a)&&0<a.nodeType};goog.dom.isElement=function(a){return goog.isObject(a)&&a.nodeType==goog.dom.NodeType.ELEMENT};\ngoog.dom.isWindow=function(a){return goog.isObject(a)&&a.window==a};goog.dom.getParentElement=function(a){if(goog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY&&!(goog.userAgent.IE&&goog.userAgent.isVersionOrHigher(\"9\")&&!goog.userAgent.isVersionOrHigher(\"10\")&&goog.global.SVGElement&&a instanceof goog.global.SVGElement))return a.parentElement;a=a.parentNode;return goog.dom.isElement(a)?a:null};\ngoog.dom.contains=function(a,b){if(a.contains&&b.nodeType==goog.dom.NodeType.ELEMENT)return a==b||a.contains(b);if(\"undefined\"!=typeof a.compareDocumentPosition)return a==b||Boolean(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a};\ngoog.dom.compareNodeOrder=function(a,b){if(a==b)return 0;if(a.compareDocumentPosition)return a.compareDocumentPosition(b)&2?1:-1;if(goog.userAgent.IE&&!goog.userAgent.isDocumentModeOrHigher(9)){if(a.nodeType==goog.dom.NodeType.DOCUMENT)return-1;if(b.nodeType==goog.dom.NodeType.DOCUMENT)return 1}if(\"sourceIndex\"in a||a.parentNode&&\"sourceIndex\"in a.parentNode){var c=a.nodeType==goog.dom.NodeType.ELEMENT,d=b.nodeType==goog.dom.NodeType.ELEMENT;if(c&&d)return a.sourceIndex-b.sourceIndex;var e=a.parentNode,\nf=b.parentNode;return e==f?goog.dom.compareSiblingOrder_(a,b):!c&&goog.dom.contains(e,b)?-1*goog.dom.compareParentsDescendantNodeIe_(a,b):!d&&goog.dom.contains(f,a)?goog.dom.compareParentsDescendantNodeIe_(b,a):(c?a.sourceIndex:e.sourceIndex)-(d?b.sourceIndex:f.sourceIndex)}d=goog.dom.getOwnerDocument(a);c=d.createRange();c.selectNode(a);c.collapse(!0);d=d.createRange();d.selectNode(b);d.collapse(!0);return c.compareBoundaryPoints(goog.global.Range.START_TO_END,d)};\ngoog.dom.compareParentsDescendantNodeIe_=function(a,b){var c=a.parentNode;if(c==b)return-1;for(var d=b;d.parentNode!=c;)d=d.parentNode;return goog.dom.compareSiblingOrder_(d,a)};goog.dom.compareSiblingOrder_=function(a,b){for(var c=b;c=c.previousSibling;)if(c==a)return-1;return 1};\ngoog.dom.findCommonAncestor=function(a){var b,c=arguments.length;if(!c)return null;if(1==c)return arguments[0];var d=[],e=Infinity;for(b=0;b<c;b++){for(var f=[],g=arguments[b];g;)f.unshift(g),g=g.parentNode;d.push(f);e=Math.min(e,f.length)}f=null;for(b=0;b<e;b++){for(var g=d[0][b],h=1;h<c;h++)if(g!=d[h][b])return f;f=g}return f};goog.dom.getOwnerDocument=function(a){return a.nodeType==goog.dom.NodeType.DOCUMENT?a:a.ownerDocument||a.document};\ngoog.dom.getFrameContentDocument=function(a){return a.contentDocument||a.contentWindow.document};goog.dom.getFrameContentWindow=function(a){return a.contentWindow||goog.dom.getWindow(goog.dom.getFrameContentDocument(a))};\ngoog.dom.setTextContent=function(a,b){goog.asserts.assert(null!=a,\"goog.dom.setTextContent expects a non-null value for node\");if(\"textContent\"in a)a.textContent=b;else if(a.nodeType==goog.dom.NodeType.TEXT)a.data=b;else if(a.firstChild&&a.firstChild.nodeType==goog.dom.NodeType.TEXT){for(;a.lastChild!=a.firstChild;)a.removeChild(a.lastChild);a.firstChild.data=b}else{goog.dom.removeChildren(a);var c=goog.dom.getOwnerDocument(a);a.appendChild(c.createTextNode(String(b)))}};\ngoog.dom.getOuterHtml=function(a){if(\"outerHTML\"in a)return a.outerHTML;var b=goog.dom.getOwnerDocument(a).createElement(\"div\");b.appendChild(a.cloneNode(!0));return b.innerHTML};goog.dom.findNode=function(a,b){var c=[];return goog.dom.findNodes_(a,b,c,!0)?c[0]:void 0};goog.dom.findNodes=function(a,b){var c=[];goog.dom.findNodes_(a,b,c,!1);return c};goog.dom.findNodes_=function(a,b,c,d){if(null!=a)for(a=a.firstChild;a;){if(b(a)&&(c.push(a),d)||goog.dom.findNodes_(a,b,c,d))return!0;a=a.nextSibling}return!1};\ngoog.dom.TAGS_TO_IGNORE_={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1};goog.dom.PREDEFINED_TAG_VALUES_={IMG:\" \",BR:\"\\n\"};goog.dom.isFocusableTabIndex=function(a){return goog.dom.hasSpecifiedTabIndex_(a)&&goog.dom.isTabIndexFocusable_(a)};goog.dom.setFocusableTabIndex=function(a,b){b?a.tabIndex=0:(a.tabIndex=-1,a.removeAttribute(\"tabIndex\"))};\ngoog.dom.isFocusable=function(a){var b;return(b=goog.dom.nativelySupportsFocus_(a)?!a.disabled&&(!goog.dom.hasSpecifiedTabIndex_(a)||goog.dom.isTabIndexFocusable_(a)):goog.dom.isFocusableTabIndex(a))&&goog.userAgent.IE?goog.dom.hasNonZeroBoundingRect_(a):b};goog.dom.hasSpecifiedTabIndex_=function(a){a=a.getAttributeNode(\"tabindex\");return goog.isDefAndNotNull(a)&&a.specified};goog.dom.isTabIndexFocusable_=function(a){a=a.tabIndex;return goog.isNumber(a)&&0<=a&&32768>a};\ngoog.dom.nativelySupportsFocus_=function(a){return a.tagName==goog.dom.TagName.A||a.tagName==goog.dom.TagName.INPUT||a.tagName==goog.dom.TagName.TEXTAREA||a.tagName==goog.dom.TagName.SELECT||a.tagName==goog.dom.TagName.BUTTON};goog.dom.hasNonZeroBoundingRect_=function(a){a=goog.isFunction(a.getBoundingClientRect)?a.getBoundingClientRect():{height:a.offsetHeight,width:a.offsetWidth};return goog.isDefAndNotNull(a)&&0<a.height&&0<a.width};\ngoog.dom.getTextContent=function(a){if(goog.dom.BrowserFeature.CAN_USE_INNER_TEXT&&\"innerText\"in a)a=goog.string.canonicalizeNewlines(a.innerText);else{var b=[];goog.dom.getTextContent_(a,b,!0);a=b.join(\"\")}a=a.replace(/ \\xAD /g,\" \").replace(/\\xAD/g,\"\");a=a.replace(/\\u200B/g,\"\");goog.dom.BrowserFeature.CAN_USE_INNER_TEXT||(a=a.replace(/ +/g,\" \"));\" \"!=a&&(a=a.replace(/^\\s*/,\"\"));return a};goog.dom.getRawTextContent=function(a){var b=[];goog.dom.getTextContent_(a,b,!1);return b.join(\"\")};\ngoog.dom.getTextContent_=function(a,b,c){if(!(a.nodeName in goog.dom.TAGS_TO_IGNORE_))if(a.nodeType==goog.dom.NodeType.TEXT)c?b.push(String(a.nodeValue).replace(/(\\r\\n|\\r|\\n)/g,\"\")):b.push(a.nodeValue);else if(a.nodeName in goog.dom.PREDEFINED_TAG_VALUES_)b.push(goog.dom.PREDEFINED_TAG_VALUES_[a.nodeName]);else for(a=a.firstChild;a;)goog.dom.getTextContent_(a,b,c),a=a.nextSibling};goog.dom.getNodeTextLength=function(a){return goog.dom.getTextContent(a).length};\ngoog.dom.getNodeTextOffset=function(a,b){for(var c=b||goog.dom.getOwnerDocument(a).body,d=[];a&&a!=c;){for(var e=a;e=e.previousSibling;)d.unshift(goog.dom.getTextContent(e));a=a.parentNode}return goog.string.trimLeft(d.join(\"\")).replace(/ +/g,\" \").length};\ngoog.dom.getNodeAtOffset=function(a,b,c){a=[a];for(var d=0,e=null;0<a.length&&d<b;)if(e=a.pop(),!(e.nodeName in goog.dom.TAGS_TO_IGNORE_))if(e.nodeType==goog.dom.NodeType.TEXT)var f=e.nodeValue.replace(/(\\r\\n|\\r|\\n)/g,\"\").replace(/ +/g,\" \"),d=d+f.length;else if(e.nodeName in goog.dom.PREDEFINED_TAG_VALUES_)d+=goog.dom.PREDEFINED_TAG_VALUES_[e.nodeName].length;else for(f=e.childNodes.length-1;0<=f;f--)a.push(e.childNodes[f]);goog.isObject(c)&&(c.remainder=e?e.nodeValue.length+b-d-1:0,c.node=e);return e};\ngoog.dom.isNodeList=function(a){if(a&&\"number\"==typeof a.length){if(goog.isObject(a))return\"function\"==typeof a.item||\"string\"==typeof a.item;if(goog.isFunction(a))return\"function\"==typeof a.item}return!1};goog.dom.getAncestorByTagNameAndClass=function(a,b,c){if(!b&&!c)return null;var d=b?b.toUpperCase():null;return goog.dom.getAncestor(a,function(a){return(!d||a.nodeName==d)&&(!c||goog.dom.classes.has(a,c))},!0)};\ngoog.dom.getAncestorByClass=function(a,b){return goog.dom.getAncestorByTagNameAndClass(a,null,b)};goog.dom.getAncestor=function(a,b,c,d){c||(a=a.parentNode);c=null==d;for(var e=0;a&&(c||e<=d);){if(b(a))return a;a=a.parentNode;e++}return null};goog.dom.getActiveElement=function(a){try{return a&&a.activeElement}catch(b){}return null};\ngoog.dom.getPixelRatio=goog.functions.cacheReturnValue(function(){var a=goog.dom.getWindow(),b=goog.userAgent.GECKO&&goog.userAgent.MOBILE;return goog.isDef(a.devicePixelRatio)&&!b?a.devicePixelRatio:a.matchMedia?goog.dom.matchesPixelRatio_(0.75)||goog.dom.matchesPixelRatio_(1.5)||goog.dom.matchesPixelRatio_(2)||goog.dom.matchesPixelRatio_(3)||1:1});\ngoog.dom.matchesPixelRatio_=function(a){return goog.dom.getWindow().matchMedia(\"(-webkit-min-device-pixel-ratio: \"+a+\"),(min--moz-device-pixel-ratio: \"+a+\"),(min-resolution: \"+a+\"dppx)\").matches?a:0};goog.dom.DomHelper=function(a){this.document_=a||goog.global.document||document};goog.dom.DomHelper.prototype.getDomHelper=goog.dom.getDomHelper;goog.dom.DomHelper.prototype.setDocument=function(a){this.document_=a};goog.dom.DomHelper.prototype.getDocument=function(){return this.document_};\ngoog.dom.DomHelper.prototype.getElement=function(a){return goog.dom.getElementHelper_(this.document_,a)};goog.dom.DomHelper.prototype.getRequiredElement=function(a){return goog.dom.getRequiredElementHelper_(this.document_,a)};goog.dom.DomHelper.prototype.$=goog.dom.DomHelper.prototype.getElement;goog.dom.DomHelper.prototype.getElementsByTagNameAndClass=function(a,b,c){return goog.dom.getElementsByTagNameAndClass_(this.document_,a,b,c)};\ngoog.dom.DomHelper.prototype.getElementsByClass=function(a,b){return goog.dom.getElementsByClass(a,b||this.document_)};goog.dom.DomHelper.prototype.getElementByClass=function(a,b){return goog.dom.getElementByClass(a,b||this.document_)};goog.dom.DomHelper.prototype.$$=goog.dom.DomHelper.prototype.getElementsByTagNameAndClass;goog.dom.DomHelper.prototype.setProperties=goog.dom.setProperties;goog.dom.DomHelper.prototype.getViewportSize=function(a){return goog.dom.getViewportSize(a||this.getWindow())};\ngoog.dom.DomHelper.prototype.getDocumentHeight=function(){return goog.dom.getDocumentHeight_(this.getWindow())};goog.dom.DomHelper.prototype.createDom=function(a,b,c){return goog.dom.createDom_(this.document_,arguments)};goog.dom.DomHelper.prototype.$dom=goog.dom.DomHelper.prototype.createDom;goog.dom.DomHelper.prototype.createElement=function(a){return this.document_.createElement(a)};goog.dom.DomHelper.prototype.createTextNode=function(a){return this.document_.createTextNode(String(a))};\ngoog.dom.DomHelper.prototype.createTable=function(a,b,c){return goog.dom.createTable_(this.document_,a,b,!!c)};goog.dom.DomHelper.prototype.htmlToDocumentFragment=function(a){return goog.dom.htmlToDocumentFragment_(this.document_,a)};goog.dom.DomHelper.prototype.isCss1CompatMode=function(){return goog.dom.isCss1CompatMode_(this.document_)};goog.dom.DomHelper.prototype.getWindow=function(){return goog.dom.getWindow_(this.document_)};goog.dom.DomHelper.prototype.getDocumentScrollElement=function(){return goog.dom.getDocumentScrollElement_(this.document_)};\ngoog.dom.DomHelper.prototype.getDocumentScroll=function(){return goog.dom.getDocumentScroll_(this.document_)};goog.dom.DomHelper.prototype.getActiveElement=function(a){return goog.dom.getActiveElement(a||this.document_)};goog.dom.DomHelper.prototype.appendChild=goog.dom.appendChild;goog.dom.DomHelper.prototype.append=goog.dom.append;goog.dom.DomHelper.prototype.canHaveChildren=goog.dom.canHaveChildren;goog.dom.DomHelper.prototype.removeChildren=goog.dom.removeChildren;\ngoog.dom.DomHelper.prototype.insertSiblingBefore=goog.dom.insertSiblingBefore;goog.dom.DomHelper.prototype.insertSiblingAfter=goog.dom.insertSiblingAfter;goog.dom.DomHelper.prototype.insertChildAt=goog.dom.insertChildAt;goog.dom.DomHelper.prototype.removeNode=goog.dom.removeNode;goog.dom.DomHelper.prototype.replaceNode=goog.dom.replaceNode;goog.dom.DomHelper.prototype.flattenElement=goog.dom.flattenElement;goog.dom.DomHelper.prototype.getChildren=goog.dom.getChildren;\ngoog.dom.DomHelper.prototype.getFirstElementChild=goog.dom.getFirstElementChild;goog.dom.DomHelper.prototype.getLastElementChild=goog.dom.getLastElementChild;goog.dom.DomHelper.prototype.getNextElementSibling=goog.dom.getNextElementSibling;goog.dom.DomHelper.prototype.getPreviousElementSibling=goog.dom.getPreviousElementSibling;goog.dom.DomHelper.prototype.getNextNode=goog.dom.getNextNode;goog.dom.DomHelper.prototype.getPreviousNode=goog.dom.getPreviousNode;\ngoog.dom.DomHelper.prototype.isNodeLike=goog.dom.isNodeLike;goog.dom.DomHelper.prototype.isElement=goog.dom.isElement;goog.dom.DomHelper.prototype.isWindow=goog.dom.isWindow;goog.dom.DomHelper.prototype.getParentElement=goog.dom.getParentElement;goog.dom.DomHelper.prototype.contains=goog.dom.contains;goog.dom.DomHelper.prototype.compareNodeOrder=goog.dom.compareNodeOrder;goog.dom.DomHelper.prototype.findCommonAncestor=goog.dom.findCommonAncestor;goog.dom.DomHelper.prototype.getOwnerDocument=goog.dom.getOwnerDocument;\ngoog.dom.DomHelper.prototype.getFrameContentDocument=goog.dom.getFrameContentDocument;goog.dom.DomHelper.prototype.getFrameContentWindow=goog.dom.getFrameContentWindow;goog.dom.DomHelper.prototype.setTextContent=goog.dom.setTextContent;goog.dom.DomHelper.prototype.getOuterHtml=goog.dom.getOuterHtml;goog.dom.DomHelper.prototype.findNode=goog.dom.findNode;goog.dom.DomHelper.prototype.findNodes=goog.dom.findNodes;goog.dom.DomHelper.prototype.isFocusableTabIndex=goog.dom.isFocusableTabIndex;\ngoog.dom.DomHelper.prototype.setFocusableTabIndex=goog.dom.setFocusableTabIndex;goog.dom.DomHelper.prototype.isFocusable=goog.dom.isFocusable;goog.dom.DomHelper.prototype.getTextContent=goog.dom.getTextContent;goog.dom.DomHelper.prototype.getNodeTextLength=goog.dom.getNodeTextLength;goog.dom.DomHelper.prototype.getNodeTextOffset=goog.dom.getNodeTextOffset;goog.dom.DomHelper.prototype.getNodeAtOffset=goog.dom.getNodeAtOffset;goog.dom.DomHelper.prototype.isNodeList=goog.dom.isNodeList;\ngoog.dom.DomHelper.prototype.getAncestorByTagNameAndClass=goog.dom.getAncestorByTagNameAndClass;goog.dom.DomHelper.prototype.getAncestorByClass=goog.dom.getAncestorByClass;goog.dom.DomHelper.prototype.getAncestor=goog.dom.getAncestor;goog.disposable={};goog.disposable.IDisposable=function(){};goog.Disposable=function(){goog.Disposable.MONITORING_MODE!=goog.Disposable.MonitoringMode.OFF&&(goog.Disposable.INCLUDE_STACK_ON_CREATION&&(this.creationStack=Error().stack),goog.Disposable.instances_[goog.getUid(this)]=this)};goog.Disposable.MonitoringMode={OFF:0,PERMANENT:1,INTERACTIVE:2};goog.Disposable.MONITORING_MODE=0;goog.Disposable.INCLUDE_STACK_ON_CREATION=!0;goog.Disposable.instances_={};\ngoog.Disposable.getUndisposedObjects=function(){var a=[],b;for(b in goog.Disposable.instances_)goog.Disposable.instances_.hasOwnProperty(b)&&a.push(goog.Disposable.instances_[Number(b)]);return a};goog.Disposable.clearUndisposedObjects=function(){goog.Disposable.instances_={}};goog.Disposable.prototype.disposed_=!1;goog.Disposable.prototype.isDisposed=function(){return this.disposed_};goog.Disposable.prototype.getDisposed=goog.Disposable.prototype.isDisposed;\ngoog.Disposable.prototype.dispose=function(){if(!this.disposed_&&(this.disposed_=!0,this.disposeInternal(),goog.Disposable.MONITORING_MODE!=goog.Disposable.MonitoringMode.OFF)){var a=goog.getUid(this);if(goog.Disposable.MONITORING_MODE==goog.Disposable.MonitoringMode.PERMANENT&&!goog.Disposable.instances_.hasOwnProperty(a))throw Error(this+\" did not call the goog.Disposable base constructor or was disposed of after a clearUndisposedObjects call\");delete goog.Disposable.instances_[a]}};\ngoog.Disposable.prototype.registerDisposable=function(a){this.addOnDisposeCallback(goog.partial(goog.dispose,a))};goog.Disposable.prototype.addOnDisposeCallback=function(a,b){this.onDisposeCallbacks_||(this.onDisposeCallbacks_=[]);this.onDisposeCallbacks_.push(goog.bind(a,b))};goog.Disposable.prototype.disposeInternal=function(){if(this.onDisposeCallbacks_)for(;this.onDisposeCallbacks_.length;)this.onDisposeCallbacks_.shift()()};\ngoog.Disposable.isDisposed=function(a){return a&&\"function\"==typeof a.isDisposed?a.isDisposed():!1};goog.dispose=function(a){a&&\"function\"==typeof a.dispose&&a.dispose()};goog.disposeAll=function(a){for(var b=0,c=arguments.length;b<c;++b){var d=arguments[b];goog.isArrayLike(d)?goog.disposeAll.apply(null,d):goog.dispose(d)}};goog.debug.entryPointRegistry={};goog.debug.EntryPointMonitor=function(){};goog.debug.entryPointRegistry.refList_=[];goog.debug.entryPointRegistry.monitors_=[];goog.debug.entryPointRegistry.monitorsMayExist_=!1;goog.debug.entryPointRegistry.register=function(a){goog.debug.entryPointRegistry.refList_[goog.debug.entryPointRegistry.refList_.length]=a;if(goog.debug.entryPointRegistry.monitorsMayExist_)for(var b=goog.debug.entryPointRegistry.monitors_,c=0;c<b.length;c++)a(goog.bind(b[c].wrap,b[c]))};\ngoog.debug.entryPointRegistry.monitorAll=function(a){goog.debug.entryPointRegistry.monitorsMayExist_=!0;for(var b=goog.bind(a.wrap,a),c=0;c<goog.debug.entryPointRegistry.refList_.length;c++)goog.debug.entryPointRegistry.refList_[c](b);goog.debug.entryPointRegistry.monitors_.push(a)};\ngoog.debug.entryPointRegistry.unmonitorAllIfPossible=function(a){var b=goog.debug.entryPointRegistry.monitors_;goog.asserts.assert(a==b[b.length-1],\"Only the most recent monitor can be unwrapped.\");a=goog.bind(a.unwrap,a);for(var c=0;c<goog.debug.entryPointRegistry.refList_.length;c++)goog.debug.entryPointRegistry.refList_[c](a);b.length--};goog.events={};\ngoog.events.BrowserFeature={HAS_W3C_BUTTON:!goog.userAgent.IE||goog.userAgent.isDocumentModeOrHigher(9),HAS_W3C_EVENT_SUPPORT:!goog.userAgent.IE||goog.userAgent.isDocumentModeOrHigher(9),SET_KEY_CODE_TO_PREVENT_DEFAULT:goog.userAgent.IE&&!goog.userAgent.isVersionOrHigher(\"9\"),HAS_NAVIGATOR_ONLINE_PROPERTY:!goog.userAgent.WEBKIT||goog.userAgent.isVersionOrHigher(\"528\"),HAS_HTML5_NETWORK_EVENT_SUPPORT:goog.userAgent.GECKO&&goog.userAgent.isVersionOrHigher(\"1.9b\")||goog.userAgent.IE&&goog.userAgent.isVersionOrHigher(\"8\")||\ngoog.userAgent.OPERA&&goog.userAgent.isVersionOrHigher(\"9.5\")||goog.userAgent.WEBKIT&&goog.userAgent.isVersionOrHigher(\"528\"),HTML5_NETWORK_EVENTS_FIRE_ON_BODY:goog.userAgent.GECKO&&!goog.userAgent.isVersionOrHigher(\"8\")||goog.userAgent.IE&&!goog.userAgent.isVersionOrHigher(\"9\"),TOUCH_ENABLED:\"ontouchstart\"in goog.global||!!(goog.global.document&&document.documentElement&&\"ontouchstart\"in document.documentElement)||!(!goog.global.navigator||!goog.global.navigator.msMaxTouchPoints)};goog.events.Event=function(a,b){this.type=a;this.currentTarget=this.target=b};goog.events.Event.prototype.disposeInternal=function(){};goog.events.Event.prototype.dispose=function(){};goog.events.Event.prototype.propagationStopped_=!1;goog.events.Event.prototype.defaultPrevented=!1;goog.events.Event.prototype.returnValue_=!0;goog.events.Event.prototype.stopPropagation=function(){this.propagationStopped_=!0};\ngoog.events.Event.prototype.preventDefault=function(){this.defaultPrevented=!0;this.returnValue_=!1};goog.events.Event.stopPropagation=function(a){a.stopPropagation()};goog.events.Event.preventDefault=function(a){a.preventDefault()};goog.events.getVendorPrefixedName_=function(a){return goog.userAgent.WEBKIT?\"webkit\"+a:goog.userAgent.OPERA?\"o\"+a.toLowerCase():a.toLowerCase()};\ngoog.events.EventType={CLICK:\"click\",DBLCLICK:\"dblclick\",MOUSEDOWN:\"mousedown\",MOUSEUP:\"mouseup\",MOUSEOVER:\"mouseover\",MOUSEOUT:\"mouseout\",MOUSEMOVE:\"mousemove\",MOUSEENTER:\"mouseenter\",MOUSELEAVE:\"mouseleave\",SELECTSTART:\"selectstart\",KEYPRESS:\"keypress\",KEYDOWN:\"keydown\",KEYUP:\"keyup\",BLUR:\"blur\",FOCUS:\"focus\",DEACTIVATE:\"deactivate\",FOCUSIN:goog.userAgent.IE?\"focusin\":\"DOMFocusIn\",FOCUSOUT:goog.userAgent.IE?\"focusout\":\"DOMFocusOut\",CHANGE:\"change\",SELECT:\"select\",SUBMIT:\"submit\",INPUT:\"input\",PROPERTYCHANGE:\"propertychange\",\nDRAGSTART:\"dragstart\",DRAG:\"drag\",DRAGENTER:\"dragenter\",DRAGOVER:\"dragover\",DRAGLEAVE:\"dragleave\",DROP:\"drop\",DRAGEND:\"dragend\",TOUCHSTART:\"touchstart\",TOUCHMOVE:\"touchmove\",TOUCHEND:\"touchend\",TOUCHCANCEL:\"touchcancel\",BEFOREUNLOAD:\"beforeunload\",CONSOLEMESSAGE:\"consolemessage\",CONTEXTMENU:\"contextmenu\",DOMCONTENTLOADED:\"DOMContentLoaded\",ERROR:\"error\",HELP:\"help\",LOAD:\"load\",LOSECAPTURE:\"losecapture\",ORIENTATIONCHANGE:\"orientationchange\",READYSTATECHANGE:\"readystatechange\",RESIZE:\"resize\",SCROLL:\"scroll\",\nUNLOAD:\"unload\",HASHCHANGE:\"hashchange\",PAGEHIDE:\"pagehide\",PAGESHOW:\"pageshow\",POPSTATE:\"popstate\",COPY:\"copy\",PASTE:\"paste\",CUT:\"cut\",BEFORECOPY:\"beforecopy\",BEFORECUT:\"beforecut\",BEFOREPASTE:\"beforepaste\",ONLINE:\"online\",OFFLINE:\"offline\",MESSAGE:\"message\",CONNECT:\"connect\",ANIMATIONSTART:goog.events.getVendorPrefixedName_(\"AnimationStart\"),ANIMATIONEND:goog.events.getVendorPrefixedName_(\"AnimationEnd\"),ANIMATIONITERATION:goog.events.getVendorPrefixedName_(\"AnimationIteration\"),TRANSITIONEND:goog.events.getVendorPrefixedName_(\"TransitionEnd\"),\nPOINTERDOWN:\"pointerdown\",POINTERUP:\"pointerup\",POINTERCANCEL:\"pointercancel\",POINTERMOVE:\"pointermove\",POINTEROVER:\"pointerover\",POINTEROUT:\"pointerout\",POINTERENTER:\"pointerenter\",POINTERLEAVE:\"pointerleave\",GOTPOINTERCAPTURE:\"gotpointercapture\",LOSTPOINTERCAPTURE:\"lostpointercapture\",MSGESTURECHANGE:\"MSGestureChange\",MSGESTUREEND:\"MSGestureEnd\",MSGESTUREHOLD:\"MSGestureHold\",MSGESTURESTART:\"MSGestureStart\",MSGESTURETAP:\"MSGestureTap\",MSGOTPOINTERCAPTURE:\"MSGotPointerCapture\",MSINERTIASTART:\"MSInertiaStart\",\nMSLOSTPOINTERCAPTURE:\"MSLostPointerCapture\",MSPOINTERCANCEL:\"MSPointerCancel\",MSPOINTERDOWN:\"MSPointerDown\",MSPOINTERENTER:\"MSPointerEnter\",MSPOINTERHOVER:\"MSPointerHover\",MSPOINTERLEAVE:\"MSPointerLeave\",MSPOINTERMOVE:\"MSPointerMove\",MSPOINTEROUT:\"MSPointerOut\",MSPOINTEROVER:\"MSPointerOver\",MSPOINTERUP:\"MSPointerUp\",TEXTINPUT:\"textinput\",COMPOSITIONSTART:\"compositionstart\",COMPOSITIONUPDATE:\"compositionupdate\",COMPOSITIONEND:\"compositionend\",EXIT:\"exit\",LOADABORT:\"loadabort\",LOADCOMMIT:\"loadcommit\",\nLOADREDIRECT:\"loadredirect\",LOADSTART:\"loadstart\",LOADSTOP:\"loadstop\",RESPONSIVE:\"responsive\",SIZECHANGED:\"sizechanged\",UNRESPONSIVE:\"unresponsive\",VISIBILITYCHANGE:\"visibilitychange\"};goog.reflect={};goog.reflect.object=function(a,b){return b};goog.reflect.sinkValue=function(a){goog.reflect.sinkValue[\" \"](a);return a};goog.reflect.sinkValue[\" \"]=goog.nullFunction;goog.reflect.canAccessProperty=function(a,b){try{return goog.reflect.sinkValue(a[b]),!0}catch(c){}return!1};goog.events.BrowserEvent=function(a,b){a&&this.init(a,b)};goog.inherits(goog.events.BrowserEvent,goog.events.Event);goog.events.BrowserEvent.MouseButton={LEFT:0,MIDDLE:1,RIGHT:2};goog.events.BrowserEvent.IEButtonMap=[1,4,2];goog.events.BrowserEvent.prototype.target=null;goog.events.BrowserEvent.prototype.relatedTarget=null;goog.events.BrowserEvent.prototype.offsetX=0;goog.events.BrowserEvent.prototype.offsetY=0;goog.events.BrowserEvent.prototype.clientX=0;\ngoog.events.BrowserEvent.prototype.clientY=0;goog.events.BrowserEvent.prototype.screenX=0;goog.events.BrowserEvent.prototype.screenY=0;goog.events.BrowserEvent.prototype.button=0;goog.events.BrowserEvent.prototype.keyCode=0;goog.events.BrowserEvent.prototype.charCode=0;goog.events.BrowserEvent.prototype.ctrlKey=!1;goog.events.BrowserEvent.prototype.altKey=!1;goog.events.BrowserEvent.prototype.shiftKey=!1;goog.events.BrowserEvent.prototype.metaKey=!1;\ngoog.events.BrowserEvent.prototype.platformModifierKey=!1;goog.events.BrowserEvent.prototype.event_=null;\ngoog.events.BrowserEvent.prototype.init=function(a,b){var c=this.type=a.type;goog.events.Event.call(this,c);this.target=a.target||a.srcElement;this.currentTarget=b;var d=a.relatedTarget;d?goog.userAgent.GECKO&&(goog.reflect.canAccessProperty(d,\"nodeName\")||(d=null)):c==goog.events.EventType.MOUSEOVER?d=a.fromElement:c==goog.events.EventType.MOUSEOUT&&(d=a.toElement);this.relatedTarget=d;this.offsetX=goog.userAgent.WEBKIT||void 0!==a.offsetX?a.offsetX:a.layerX;this.offsetY=goog.userAgent.WEBKIT||void 0!==\na.offsetY?a.offsetY:a.layerY;this.clientX=void 0!==a.clientX?a.clientX:a.pageX;this.clientY=void 0!==a.clientY?a.clientY:a.pageY;this.screenX=a.screenX||0;this.screenY=a.screenY||0;this.button=a.button;this.keyCode=a.keyCode||0;this.charCode=a.charCode||(\"keypress\"==c?a.keyCode:0);this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.platformModifierKey=goog.userAgent.MAC?a.metaKey:a.ctrlKey;this.state=a.state;this.event_=a;a.defaultPrevented&&this.preventDefault();\ndelete this.propagationStopped_};goog.events.BrowserEvent.prototype.isButton=function(a){return goog.events.BrowserFeature.HAS_W3C_BUTTON?this.event_.button==a:\"click\"==this.type?a==goog.events.BrowserEvent.MouseButton.LEFT:!!(this.event_.button&goog.events.BrowserEvent.IEButtonMap[a])};goog.events.BrowserEvent.prototype.isMouseActionButton=function(){return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT)&&!(goog.userAgent.WEBKIT&&goog.userAgent.MAC&&this.ctrlKey)};\ngoog.events.BrowserEvent.prototype.stopPropagation=function(){goog.events.BrowserEvent.superClass_.stopPropagation.call(this);this.event_.stopPropagation?this.event_.stopPropagation():this.event_.cancelBubble=!0};\ngoog.events.BrowserEvent.prototype.preventDefault=function(){goog.events.BrowserEvent.superClass_.preventDefault.call(this);var a=this.event_;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};goog.events.BrowserEvent.prototype.getBrowserEvent=function(){return this.event_};goog.events.BrowserEvent.prototype.disposeInternal=function(){};goog.events.EventId=function(a){this.id=a};goog.events.EventId.prototype.toString=function(){return this.id};goog.events.Listenable=function(){};goog.events.Listenable.IMPLEMENTED_BY_PROP=\"closure_listenable_\"+(1E6*Math.random()|0);goog.events.Listenable.addImplementation=function(a){a.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP]=!0};goog.events.Listenable.isImplementedBy=function(a){try{return!(!a||!a[goog.events.Listenable.IMPLEMENTED_BY_PROP])}catch(b){return!1}};goog.events.ListenableKey=function(){};goog.events.ListenableKey.counter_=0;goog.events.ListenableKey.reserveKey=function(){return++goog.events.ListenableKey.counter_};goog.events.Listener=function(a,b,c,d,e,f){goog.events.Listener.ENABLE_MONITORING&&(this.creationStack=Error().stack);this.listener=a;this.proxy=b;this.src=c;this.type=d;this.capture=!!e;this.handler=f;this.key=goog.events.ListenableKey.reserveKey();this.removed=this.callOnce=!1};goog.events.Listener.ENABLE_MONITORING=!1;goog.events.Listener.prototype.markAsRemoved=function(){this.removed=!0;this.handler=this.src=this.proxy=this.listener=null};goog.events.ListenerMap=function(a){this.src=a;this.listeners={};this.typeCount_=0};goog.events.ListenerMap.prototype.getTypeCount=function(){return this.typeCount_};goog.events.ListenerMap.prototype.getListenerCount=function(){var a=0,b;for(b in this.listeners)a+=this.listeners[b].length;return a};\ngoog.events.ListenerMap.prototype.add=function(a,b,c,d,e){var f=this.listeners[a];f||(f=this.listeners[a]=[],this.typeCount_++);var g=goog.events.ListenerMap.findListenerIndex_(f,b,d,e);-1<g?(a=f[g],c||(a.callOnce=!1)):(a=new goog.events.Listener(b,null,this.src,a,!!d,e),a.callOnce=c,f.push(a));return a};\ngoog.events.ListenerMap.prototype.remove=function(a,b,c,d){if(!(a in this.listeners))return!1;var e=this.listeners[a];b=goog.events.ListenerMap.findListenerIndex_(e,b,c,d);return-1<b?(e[b].markAsRemoved(),goog.array.removeAt(e,b),0==e.length&&(delete this.listeners[a],this.typeCount_--),!0):!1};\ngoog.events.ListenerMap.prototype.removeByKey=function(a){var b=a.type;if(!(b in this.listeners))return!1;var c=goog.array.remove(this.listeners[b],a);c&&(a.markAsRemoved(),0==this.listeners[b].length&&(delete this.listeners[b],this.typeCount_--));return c};goog.events.ListenerMap.prototype.removeAll=function(a){var b=0,c;for(c in this.listeners)if(!a||c==a){for(var d=this.listeners[c],e=0;e<d.length;e++)++b,d[e].markAsRemoved();delete this.listeners[c];this.typeCount_--}return b};\ngoog.events.ListenerMap.prototype.getListeners=function(a,b){var c=this.listeners[a],d=[];if(c)for(var e=0;e<c.length;++e){var f=c[e];f.capture==b&&d.push(f)}return d};goog.events.ListenerMap.prototype.getListener=function(a,b,c,d){a=this.listeners[a];var e=-1;a&&(e=goog.events.ListenerMap.findListenerIndex_(a,b,c,d));return-1<e?a[e]:null};\ngoog.events.ListenerMap.prototype.hasListener=function(a,b){var c=goog.isDef(a),d=goog.isDef(b);return goog.object.some(this.listeners,function(e,f){for(var g=0;g<e.length;++g)if(!(c&&e[g].type!=a||d&&e[g].capture!=b))return!0;return!1})};goog.events.ListenerMap.findListenerIndex_=function(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.removed&&f.listener==b&&f.capture==!!c&&f.handler==d)return e}return-1};goog.events.listeners_={};goog.events.LISTENER_MAP_PROP_=\"closure_lm_\"+(1E6*Math.random()|0);goog.events.onString_=\"on\";goog.events.onStringMap_={};goog.events.CaptureSimulationMode={OFF_AND_FAIL:0,OFF_AND_SILENT:1,ON:2};goog.events.CAPTURE_SIMULATION_MODE=2;goog.events.listenerCountEstimate_=0;\ngoog.events.listen=function(a,b,c,d,e){if(goog.isArray(b)){for(var f=0;f<b.length;f++)goog.events.listen(a,b[f],c,d,e);return null}c=goog.events.wrapListener_(c);return goog.events.Listenable.isImplementedBy(a)?a.listen(b,c,d,e):goog.events.listen_(a,b,c,!1,d,e)};\ngoog.events.listen_=function(a,b,c,d,e,f){if(!b)throw Error(\"Invalid event type\");var g=!!e;if(g&&!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT){if(goog.events.CAPTURE_SIMULATION_MODE==goog.events.CaptureSimulationMode.OFF_AND_FAIL)return goog.asserts.fail(\"Can not register capture listener in IE8-.\"),null;if(goog.events.CAPTURE_SIMULATION_MODE==goog.events.CaptureSimulationMode.OFF_AND_SILENT)return null}var h=goog.events.getListenerMap_(a);h||(a[goog.events.LISTENER_MAP_PROP_]=h=new goog.events.ListenerMap(a));\nc=h.add(b,c,d,e,f);if(c.proxy)return c;d=goog.events.getProxy();c.proxy=d;d.src=a;d.listener=c;a.addEventListener?a.addEventListener(b,d,g):a.attachEvent(goog.events.getOnString_(b),d);goog.events.listenerCountEstimate_++;return c};goog.events.getProxy=function(){var a=goog.events.handleBrowserEvent_,b=goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT?function(c){return a.call(b.src,b.listener,c)}:function(c){c=a.call(b.src,b.listener,c);if(!c)return c};return b};\ngoog.events.listenOnce=function(a,b,c,d,e){if(goog.isArray(b)){for(var f=0;f<b.length;f++)goog.events.listenOnce(a,b[f],c,d,e);return null}c=goog.events.wrapListener_(c);return goog.events.Listenable.isImplementedBy(a)?a.listenOnce(b,c,d,e):goog.events.listen_(a,b,c,!0,d,e)};goog.events.listenWithWrapper=function(a,b,c,d,e){b.listen(a,c,d,e)};\ngoog.events.unlisten=function(a,b,c,d,e){if(goog.isArray(b)){for(var f=0;f<b.length;f++)goog.events.unlisten(a,b[f],c,d,e);return null}c=goog.events.wrapListener_(c);if(goog.events.Listenable.isImplementedBy(a))return a.unlisten(b,c,d,e);if(!a)return!1;d=!!d;if(a=goog.events.getListenerMap_(a))if(b=a.getListener(b,c,d,e))return goog.events.unlistenByKey(b);return!1};\ngoog.events.unlistenByKey=function(a){if(goog.isNumber(a)||!a||a.removed)return!1;var b=a.src;if(goog.events.Listenable.isImplementedBy(b))return b.unlistenByKey(a);var c=a.type,d=a.proxy;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent&&b.detachEvent(goog.events.getOnString_(c),d);goog.events.listenerCountEstimate_--;(c=goog.events.getListenerMap_(b))?(c.removeByKey(a),0==c.getTypeCount()&&(c.src=null,b[goog.events.LISTENER_MAP_PROP_]=null)):a.markAsRemoved();return!0};\ngoog.events.unlistenWithWrapper=function(a,b,c,d,e){b.unlisten(a,c,d,e)};goog.events.removeAll=function(a,b){if(!a)return 0;if(goog.events.Listenable.isImplementedBy(a))return a.removeAllListeners(b);var c=goog.events.getListenerMap_(a);if(!c)return 0;var d=0,e;for(e in c.listeners)if(!b||e==b)for(var f=goog.array.clone(c.listeners[e]),g=0;g<f.length;++g)goog.events.unlistenByKey(f[g])&&++d;return d};goog.events.removeAllNativeListeners=function(){return goog.events.listenerCountEstimate_=0};\ngoog.events.getListeners=function(a,b,c){return goog.events.Listenable.isImplementedBy(a)?a.getListeners(b,c):a?(a=goog.events.getListenerMap_(a))?a.getListeners(b,c):[]:[]};goog.events.getListener=function(a,b,c,d,e){c=goog.events.wrapListener_(c);d=!!d;return goog.events.Listenable.isImplementedBy(a)?a.getListener(b,c,d,e):a?(a=goog.events.getListenerMap_(a))?a.getListener(b,c,d,e):null:null};\ngoog.events.hasListener=function(a,b,c){if(goog.events.Listenable.isImplementedBy(a))return a.hasListener(b,c);a=goog.events.getListenerMap_(a);return!!a&&a.hasListener(b,c)};goog.events.expose=function(a){var b=[],c;for(c in a)a[c]&&a[c].id?b.push(c+\" = \"+a[c]+\" (\"+a[c].id+\")\"):b.push(c+\" = \"+a[c]);return b.join(\"\\n\")};goog.events.getOnString_=function(a){return a in goog.events.onStringMap_?goog.events.onStringMap_[a]:goog.events.onStringMap_[a]=goog.events.onString_+a};\ngoog.events.fireListeners=function(a,b,c,d){return goog.events.Listenable.isImplementedBy(a)?a.fireListeners(b,c,d):goog.events.fireListeners_(a,b,c,d)};goog.events.fireListeners_=function(a,b,c,d){var e=1;if(a=goog.events.getListenerMap_(a))if(b=a.listeners[b])for(b=goog.array.clone(b),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.removed&&(e&=!1!==goog.events.fireListener(f,d))}return Boolean(e)};\ngoog.events.fireListener=function(a,b){var c=a.listener,d=a.handler||a.src;a.callOnce&&goog.events.unlistenByKey(a);return c.call(d,b)};goog.events.getTotalListenerCount=function(){return goog.events.listenerCountEstimate_};goog.events.dispatchEvent=function(a,b){goog.asserts.assert(goog.events.Listenable.isImplementedBy(a),\"Can not use goog.events.dispatchEvent with non-goog.events.Listenable instance.\");return a.dispatchEvent(b)};\ngoog.events.protectBrowserEventEntryPoint=function(a){goog.events.handleBrowserEvent_=a.protectEntryPoint(goog.events.handleBrowserEvent_)};\ngoog.events.handleBrowserEvent_=function(a,b){if(a.removed)return!0;if(!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT){var c=b||goog.getObjectByName(\"window.event\"),d=new goog.events.BrowserEvent(c,this),e=!0;if(goog.events.CAPTURE_SIMULATION_MODE==goog.events.CaptureSimulationMode.ON){if(!goog.events.isMarkedIeEvent_(c)){goog.events.markIeEvent_(c);for(var c=[],f=d.currentTarget;f;f=f.parentNode)c.push(f);for(var f=a.type,g=c.length-1;!d.propagationStopped_&&0<=g;g--)d.currentTarget=c[g],e&=goog.events.fireListeners_(c[g],\nf,!0,d);for(g=0;!d.propagationStopped_&&g<c.length;g++)d.currentTarget=c[g],e&=goog.events.fireListeners_(c[g],f,!1,d)}}else e=goog.events.fireListener(a,d);return e}return goog.events.fireListener(a,new goog.events.BrowserEvent(b,this))};goog.events.markIeEvent_=function(a){var b=!1;if(0==a.keyCode)try{a.keyCode=-1;return}catch(c){b=!0}if(b||void 0==a.returnValue)a.returnValue=!0};goog.events.isMarkedIeEvent_=function(a){return 0>a.keyCode||void 0!=a.returnValue};goog.events.uniqueIdCounter_=0;\ngoog.events.getUniqueId=function(a){return a+\"_\"+goog.events.uniqueIdCounter_++};goog.events.getListenerMap_=function(a){a=a[goog.events.LISTENER_MAP_PROP_];return a instanceof goog.events.ListenerMap?a:null};goog.events.LISTENER_WRAPPER_PROP_=\"__closure_events_fn_\"+(1E9*Math.random()>>>0);\ngoog.events.wrapListener_=function(a){goog.asserts.assert(a,\"Listener can not be null.\");if(goog.isFunction(a))return a;goog.asserts.assert(a.handleEvent,\"An object listener must have handleEvent method.\");return a[goog.events.LISTENER_WRAPPER_PROP_]||(a[goog.events.LISTENER_WRAPPER_PROP_]=function(b){return a.handleEvent(b)})};goog.debug.entryPointRegistry.register(function(a){goog.events.handleBrowserEvent_=a(goog.events.handleBrowserEvent_)});goog.events.EventTarget=function(){goog.Disposable.call(this);this.eventTargetListeners_=new goog.events.ListenerMap(this);this.actualEventTarget_=this};goog.inherits(goog.events.EventTarget,goog.Disposable);goog.events.Listenable.addImplementation(goog.events.EventTarget);goog.events.EventTarget.MAX_ANCESTORS_=1E3;goog.events.EventTarget.prototype.parentEventTarget_=null;goog.events.EventTarget.prototype.getParentEventTarget=function(){return this.parentEventTarget_};\ngoog.events.EventTarget.prototype.setParentEventTarget=function(a){this.parentEventTarget_=a};goog.events.EventTarget.prototype.addEventListener=function(a,b,c,d){goog.events.listen(this,a,b,c,d)};goog.events.EventTarget.prototype.removeEventListener=function(a,b,c,d){goog.events.unlisten(this,a,b,c,d)};\ngoog.events.EventTarget.prototype.dispatchEvent=function(a){this.assertInitialized_();var b,c=this.getParentEventTarget();if(c){b=[];for(var d=1;c;c=c.getParentEventTarget())b.push(c),goog.asserts.assert(++d<goog.events.EventTarget.MAX_ANCESTORS_,\"infinite loop\")}return goog.events.EventTarget.dispatchEventInternal_(this.actualEventTarget_,a,b)};\ngoog.events.EventTarget.prototype.disposeInternal=function(){goog.events.EventTarget.superClass_.disposeInternal.call(this);this.removeAllListeners();this.parentEventTarget_=null};goog.events.EventTarget.prototype.listen=function(a,b,c,d){this.assertInitialized_();return this.eventTargetListeners_.add(String(a),b,!1,c,d)};goog.events.EventTarget.prototype.listenOnce=function(a,b,c,d){return this.eventTargetListeners_.add(String(a),b,!0,c,d)};\ngoog.events.EventTarget.prototype.unlisten=function(a,b,c,d){return this.eventTargetListeners_.remove(String(a),b,c,d)};goog.events.EventTarget.prototype.unlistenByKey=function(a){return this.eventTargetListeners_.removeByKey(a)};goog.events.EventTarget.prototype.removeAllListeners=function(a){return this.eventTargetListeners_?this.eventTargetListeners_.removeAll(a):0};\ngoog.events.EventTarget.prototype.fireListeners=function(a,b,c){a=this.eventTargetListeners_.listeners[String(a)];if(!a)return!0;a=goog.array.clone(a);for(var d=!0,e=0;e<a.length;++e){var f=a[e];if(f&&!f.removed&&f.capture==b){var g=f.listener,h=f.handler||f.src;f.callOnce&&this.unlistenByKey(f);d=!1!==g.call(h,c)&&d}}return d&&!1!=c.returnValue_};goog.events.EventTarget.prototype.getListeners=function(a,b){return this.eventTargetListeners_.getListeners(String(a),b)};\ngoog.events.EventTarget.prototype.getListener=function(a,b,c,d){return this.eventTargetListeners_.getListener(String(a),b,c,d)};goog.events.EventTarget.prototype.hasListener=function(a,b){var c=goog.isDef(a)?String(a):void 0;return this.eventTargetListeners_.hasListener(c,b)};goog.events.EventTarget.prototype.setTargetForTesting=function(a){this.actualEventTarget_=a};goog.events.EventTarget.prototype.assertInitialized_=function(){goog.asserts.assert(this.eventTargetListeners_,\"Event target is not initialized. Did you call the superclass (goog.events.EventTarget) constructor?\")};\ngoog.events.EventTarget.dispatchEventInternal_=function(a,b,c){var d=b.type||b;if(goog.isString(b))b=new goog.events.Event(b,a);else if(b instanceof goog.events.Event)b.target=b.target||a;else{var e=b;b=new goog.events.Event(d,a);goog.object.extend(b,e)}var e=!0,f;if(c)for(var g=c.length-1;!b.propagationStopped_&&0<=g;g--)f=b.currentTarget=c[g],e=f.fireListeners(d,!0,b)&&e;b.propagationStopped_||(f=b.currentTarget=a,e=f.fireListeners(d,!0,b)&&e,b.propagationStopped_||(e=f.fireListeners(d,!1,b)&&e));\nif(c)for(g=0;!b.propagationStopped_&&g<c.length;g++)f=b.currentTarget=c[g],e=f.fireListeners(d,!1,b)&&e;return e};goog.Timer=function(a,b){goog.events.EventTarget.call(this);this.interval_=a||1;this.timerObject_=b||goog.Timer.defaultTimerObject;this.boundTick_=goog.bind(this.tick_,this);this.last_=goog.now()};goog.inherits(goog.Timer,goog.events.EventTarget);goog.Timer.MAX_TIMEOUT_=2147483647;goog.Timer.prototype.enabled=!1;goog.Timer.defaultTimerObject=goog.global;goog.Timer.intervalScale=0.8;goog.Timer.prototype.timer_=null;goog.Timer.prototype.getInterval=function(){return this.interval_};\ngoog.Timer.prototype.setInterval=function(a){this.interval_=a;this.timer_&&this.enabled?(this.stop(),this.start()):this.timer_&&this.stop()};\ngoog.Timer.prototype.tick_=function(){if(this.enabled){var a=goog.now()-this.last_;0<a&&a<this.interval_*goog.Timer.intervalScale?this.timer_=this.timerObject_.setTimeout(this.boundTick_,this.interval_-a):(this.timer_&&(this.timerObject_.clearTimeout(this.timer_),this.timer_=null),this.dispatchTick(),this.enabled&&(this.timer_=this.timerObject_.setTimeout(this.boundTick_,this.interval_),this.last_=goog.now()))}};goog.Timer.prototype.dispatchTick=function(){this.dispatchEvent(goog.Timer.TICK)};\ngoog.Timer.prototype.start=function(){this.enabled=!0;this.timer_||(this.timer_=this.timerObject_.setTimeout(this.boundTick_,this.interval_),this.last_=goog.now())};goog.Timer.prototype.stop=function(){this.enabled=!1;this.timer_&&(this.timerObject_.clearTimeout(this.timer_),this.timer_=null)};goog.Timer.prototype.disposeInternal=function(){goog.Timer.superClass_.disposeInternal.call(this);this.stop();delete this.timerObject_};goog.Timer.TICK=\"tick\";\ngoog.Timer.callOnce=function(a,b,c){if(goog.isFunction(a))c&&(a=goog.bind(a,c));else if(a&&\"function\"==typeof a.handleEvent)a=goog.bind(a.handleEvent,a);else throw Error(\"Invalid listener argument\");return b>goog.Timer.MAX_TIMEOUT_?-1:goog.Timer.defaultTimerObject.setTimeout(a,b||0)};goog.Timer.clear=function(a){goog.Timer.defaultTimerObject.clearTimeout(a)};goog.json={};goog.json.isValid_=function(a){return/^\\s*$/.test(a)?!1:/^[\\],:{}\\s\\u2028\\u2029]*$/.test(a.replace(/\\\\[\"\\\\\\/bfnrtu]/g,\"@\").replace(/\"[^\"\\\\\\n\\r\\u2028\\u2029\\x00-\\x08\\x0a-\\x1f]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g,\"]\").replace(/(?:^|:|,)(?:[\\s\\u2028\\u2029]*\\[)+/g,\"\"))};goog.json.parse=function(a){a=String(a);if(goog.json.isValid_(a))try{return eval(\"(\"+a+\")\")}catch(b){}throw Error(\"Invalid JSON string: \"+a);};goog.json.unsafeParse=function(a){return eval(\"(\"+a+\")\")};\ngoog.json.serialize=function(a,b){return(new goog.json.Serializer(b)).serialize(a)};goog.json.Serializer=function(a){this.replacer_=a};goog.json.Serializer.prototype.serialize=function(a){var b=[];this.serialize_(a,b);return b.join(\"\")};\ngoog.json.Serializer.prototype.serialize_=function(a,b){switch(typeof a){case \"string\":this.serializeString_(a,b);break;case \"number\":this.serializeNumber_(a,b);break;case \"boolean\":b.push(a);break;case \"undefined\":b.push(\"null\");break;case \"object\":if(null==a){b.push(\"null\");break}if(goog.isArray(a)){this.serializeArray(a,b);break}this.serializeObject_(a,b);break;case \"function\":break;default:throw Error(\"Unknown type: \"+typeof a);}};\ngoog.json.Serializer.charToJsonCharCache_={'\"':'\\\\\"',\"\\\\\":\"\\\\\\\\\",\"/\":\"\\\\/\",\"\\b\":\"\\\\b\",\"\\f\":\"\\\\f\",\"\\n\":\"\\\\n\",\"\\r\":\"\\\\r\",\"\\t\":\"\\\\t\",\"\\x0B\":\"\\\\u000b\"};goog.json.Serializer.charsToReplace_=/\\uffff/.test(\"\\uffff\")?/[\\\\\\\"\\x00-\\x1f\\x7f-\\uffff]/g:/[\\\\\\\"\\x00-\\x1f\\x7f-\\xff]/g;\ngoog.json.Serializer.prototype.serializeString_=function(a,b){b.push('\"',a.replace(goog.json.Serializer.charsToReplace_,function(a){if(a in goog.json.Serializer.charToJsonCharCache_)return goog.json.Serializer.charToJsonCharCache_[a];var b=a.charCodeAt(0),e=\"\\\\u\";16>b?e+=\"000\":256>b?e+=\"00\":4096>b&&(e+=\"0\");return goog.json.Serializer.charToJsonCharCache_[a]=e+b.toString(16)}),'\"')};goog.json.Serializer.prototype.serializeNumber_=function(a,b){b.push(isFinite(a)&&!isNaN(a)?a:\"null\")};\ngoog.json.Serializer.prototype.serializeArray=function(a,b){var c=a.length;b.push(\"[\");for(var d=\"\",e=0;e<c;e++)b.push(d),d=a[e],this.serialize_(this.replacer_?this.replacer_.call(a,String(e),d):d,b),d=\",\";b.push(\"]\")};\ngoog.json.Serializer.prototype.serializeObject_=function(a,b){b.push(\"{\");var c=\"\",d;for(d in a)if(Object.prototype.hasOwnProperty.call(a,d)){var e=a[d];\"function\"!=typeof e&&(b.push(c),this.serializeString_(d,b),b.push(\":\"),this.serialize_(this.replacer_?this.replacer_.call(a,d,e):e,b),c=\",\")}b.push(\"}\")};goog.structs={};goog.structs.getCount=function(a){return\"function\"==typeof a.getCount?a.getCount():goog.isArrayLike(a)||goog.isString(a)?a.length:goog.object.getCount(a)};goog.structs.getValues=function(a){if(\"function\"==typeof a.getValues)return a.getValues();if(goog.isString(a))return a.split(\"\");if(goog.isArrayLike(a)){for(var b=[],c=a.length,d=0;d<c;d++)b.push(a[d]);return b}return goog.object.getValues(a)};\ngoog.structs.getKeys=function(a){if(\"function\"==typeof a.getKeys)return a.getKeys();if(\"function\"!=typeof a.getValues){if(goog.isArrayLike(a)||goog.isString(a)){var b=[];a=a.length;for(var c=0;c<a;c++)b.push(c);return b}return goog.object.getKeys(a)}};goog.structs.contains=function(a,b){return\"function\"==typeof a.contains?a.contains(b):\"function\"==typeof a.containsValue?a.containsValue(b):goog.isArrayLike(a)||goog.isString(a)?goog.array.contains(a,b):goog.object.containsValue(a,b)};\ngoog.structs.isEmpty=function(a){return\"function\"==typeof a.isEmpty?a.isEmpty():goog.isArrayLike(a)||goog.isString(a)?goog.array.isEmpty(a):goog.object.isEmpty(a)};goog.structs.clear=function(a){\"function\"==typeof a.clear?a.clear():goog.isArrayLike(a)?goog.array.clear(a):goog.object.clear(a)};\ngoog.structs.forEach=function(a,b,c){if(\"function\"==typeof a.forEach)a.forEach(b,c);else if(goog.isArrayLike(a)||goog.isString(a))goog.array.forEach(a,b,c);else for(var d=goog.structs.getKeys(a),e=goog.structs.getValues(a),f=e.length,g=0;g<f;g++)b.call(c,e[g],d&&d[g],a)};\ngoog.structs.filter=function(a,b,c){if(\"function\"==typeof a.filter)return a.filter(b,c);if(goog.isArrayLike(a)||goog.isString(a))return goog.array.filter(a,b,c);var d,e=goog.structs.getKeys(a),f=goog.structs.getValues(a),g=f.length;if(e){d={};for(var h=0;h<g;h++)b.call(c,f[h],e[h],a)&&(d[e[h]]=f[h])}else for(d=[],h=0;h<g;h++)b.call(c,f[h],void 0,a)&&d.push(f[h]);return d};\ngoog.structs.map=function(a,b,c){if(\"function\"==typeof a.map)return a.map(b,c);if(goog.isArrayLike(a)||goog.isString(a))return goog.array.map(a,b,c);var d,e=goog.structs.getKeys(a),f=goog.structs.getValues(a),g=f.length;if(e){d={};for(var h=0;h<g;h++)d[e[h]]=b.call(c,f[h],e[h],a)}else for(d=[],h=0;h<g;h++)d[h]=b.call(c,f[h],void 0,a);return d};\ngoog.structs.some=function(a,b,c){if(\"function\"==typeof a.some)return a.some(b,c);if(goog.isArrayLike(a)||goog.isString(a))return goog.array.some(a,b,c);for(var d=goog.structs.getKeys(a),e=goog.structs.getValues(a),f=e.length,g=0;g<f;g++)if(b.call(c,e[g],d&&d[g],a))return!0;return!1};\ngoog.structs.every=function(a,b,c){if(\"function\"==typeof a.every)return a.every(b,c);if(goog.isArrayLike(a)||goog.isString(a))return goog.array.every(a,b,c);for(var d=goog.structs.getKeys(a),e=goog.structs.getValues(a),f=e.length,g=0;g<f;g++)if(!b.call(c,e[g],d&&d[g],a))return!1;return!0};goog.structs.Collection=function(){};goog.iter={};goog.iter.StopIteration=\"StopIteration\"in goog.global?goog.global.StopIteration:Error(\"StopIteration\");goog.iter.Iterator=function(){};goog.iter.Iterator.prototype.next=function(){throw goog.iter.StopIteration;};goog.iter.Iterator.prototype.__iterator__=function(a){return this};\ngoog.iter.toIterator=function(a){if(a instanceof goog.iter.Iterator)return a;if(\"function\"==typeof a.__iterator__)return a.__iterator__(!1);if(goog.isArrayLike(a)){var b=0,c=new goog.iter.Iterator;c.next=function(){for(;;){if(b>=a.length)throw goog.iter.StopIteration;if(b in a)return a[b++];b++}};return c}throw Error(\"Not implemented\");};\ngoog.iter.forEach=function(a,b,c){if(goog.isArrayLike(a))try{goog.array.forEach(a,b,c)}catch(d){if(d!==goog.iter.StopIteration)throw d;}else{a=goog.iter.toIterator(a);try{for(;;)b.call(c,a.next(),void 0,a)}catch(e){if(e!==goog.iter.StopIteration)throw e;}}};goog.iter.filter=function(a,b,c){var d=goog.iter.toIterator(a);a=new goog.iter.Iterator;a.next=function(){for(;;){var a=d.next();if(b.call(c,a,void 0,d))return a}};return a};\ngoog.iter.range=function(a,b,c){var d=0,e=a,f=c||1;1<arguments.length&&(d=a,e=b);if(0==f)throw Error(\"Range step argument must not be zero\");var g=new goog.iter.Iterator;g.next=function(){if(0<f&&d>=e||0>f&&d<=e)throw goog.iter.StopIteration;var a=d;d+=f;return a};return g};goog.iter.join=function(a,b){return goog.iter.toArray(a).join(b)};goog.iter.map=function(a,b,c){var d=goog.iter.toIterator(a);a=new goog.iter.Iterator;a.next=function(){for(;;){var a=d.next();return b.call(c,a,void 0,d)}};return a};\ngoog.iter.reduce=function(a,b,c,d){var e=c;goog.iter.forEach(a,function(a){e=b.call(d,e,a)});return e};goog.iter.some=function(a,b,c){a=goog.iter.toIterator(a);try{for(;;)if(b.call(c,a.next(),void 0,a))return!0}catch(d){if(d!==goog.iter.StopIteration)throw d;}return!1};goog.iter.every=function(a,b,c){a=goog.iter.toIterator(a);try{for(;;)if(!b.call(c,a.next(),void 0,a))return!1}catch(d){if(d!==goog.iter.StopIteration)throw d;}return!0};\ngoog.iter.chain=function(a){var b=goog.iter.toIterator(arguments),c=new goog.iter.Iterator,d=null;c.next=function(){for(;;){if(null==d){var a=b.next();d=goog.iter.toIterator(a)}try{return d.next()}catch(c){if(c!==goog.iter.StopIteration)throw c;d=null}}};return c};goog.iter.chainFromIterable=function(a){return goog.iter.chain.apply(void 0,a)};\ngoog.iter.dropWhile=function(a,b,c){var d=goog.iter.toIterator(a);a=new goog.iter.Iterator;var e=!0;a.next=function(){for(;;){var a=d.next();if(!e||!b.call(c,a,void 0,d))return e=!1,a}};return a};goog.iter.takeWhile=function(a,b,c){var d=goog.iter.toIterator(a);a=new goog.iter.Iterator;var e=!0;a.next=function(){for(;;)if(e){var a=d.next();if(b.call(c,a,void 0,d))return a;e=!1}else throw goog.iter.StopIteration;};return a};\ngoog.iter.toArray=function(a){if(goog.isArrayLike(a))return goog.array.toArray(a);a=goog.iter.toIterator(a);var b=[];goog.iter.forEach(a,function(a){b.push(a)});return b};goog.iter.equals=function(a,b){var c=goog.iter.zipLongest({},a,b);return goog.iter.every(c,function(a){return a[0]==a[1]})};goog.iter.nextOrValue=function(a,b){try{return goog.iter.toIterator(a).next()}catch(c){if(c!=goog.iter.StopIteration)throw c;return b}};\ngoog.iter.product=function(a){if(goog.array.some(arguments,function(a){return!a.length})||!arguments.length)return new goog.iter.Iterator;var b=new goog.iter.Iterator,c=arguments,d=goog.array.repeat(0,c.length);b.next=function(){if(d){for(var a=goog.array.map(d,function(a,b){return c[b][a]}),b=d.length-1;0<=b;b--){goog.asserts.assert(d);if(d[b]<c[b].length-1){d[b]++;break}if(0==b){d=null;break}d[b]=0}return a}throw goog.iter.StopIteration;};return b};\ngoog.iter.cycle=function(a){var b=goog.iter.toIterator(a),c=[],d=0;a=new goog.iter.Iterator;var e=!1;a.next=function(){var a=null;if(!e)try{return a=b.next(),c.push(a),a}catch(g){if(g!=goog.iter.StopIteration||goog.array.isEmpty(c))throw g;e=!0}a=c[d];d=(d+1)%c.length;return a};return a};goog.iter.count=function(a,b){var c=a||0,d=goog.isDef(b)?b:1,e=new goog.iter.Iterator;e.next=function(){var a=c;c+=d;return a};return e};\ngoog.iter.repeat=function(a){var b=new goog.iter.Iterator;b.next=goog.functions.constant(a);return b};goog.iter.accumulate=function(a){var b=goog.iter.toIterator(a),c=0;a=new goog.iter.Iterator;a.next=function(){return c+=b.next()};return a};goog.iter.zip=function(a){var b=arguments,c=new goog.iter.Iterator;if(0<b.length){var d=goog.array.map(b,goog.iter.toIterator);c.next=function(){return goog.array.map(d,function(a){return a.next()})}}return c};\ngoog.iter.zipLongest=function(a,b){var c=goog.array.slice(arguments,1),d=new goog.iter.Iterator;if(0<c.length){var e=goog.array.map(c,goog.iter.toIterator);d.next=function(){var b=!1,c=goog.array.map(e,function(c){var d;try{d=c.next(),b=!0}catch(e){if(e!==goog.iter.StopIteration)throw e;d=a}return d});if(!b)throw goog.iter.StopIteration;return c}}return d};goog.iter.compress=function(a,b){var c=goog.iter.toIterator(b);return goog.iter.filter(a,function(){return!!c.next()})};\ngoog.iter.GroupByIterator_=function(a,b){this.iterator=goog.iter.toIterator(a);this.keyFunc=b||goog.functions.identity};goog.inherits(goog.iter.GroupByIterator_,goog.iter.Iterator);goog.iter.GroupByIterator_.prototype.next=function(){for(;this.currentKey==this.targetKey;)this.currentValue=this.iterator.next(),this.currentKey=this.keyFunc(this.currentValue);this.targetKey=this.currentKey;return[this.currentKey,this.groupItems_(this.targetKey)]};\ngoog.iter.GroupByIterator_.prototype.groupItems_=function(a){for(var b=[];this.currentKey==a;){b.push(this.currentValue);try{this.currentValue=this.iterator.next()}catch(c){if(c!==goog.iter.StopIteration)throw c;break}this.currentKey=this.keyFunc(this.currentValue)}return b};goog.iter.groupBy=function(a,b){return new goog.iter.GroupByIterator_(a,b)};\ngoog.iter.tee=function(a,b){var c=goog.iter.toIterator(a),d=goog.isNumber(b)?b:2,e=goog.array.map(goog.array.range(d),function(){return[]}),f=function(){var a=c.next();goog.array.forEach(e,function(b){b.push(a)})};return goog.array.map(e,function(a){var b=new goog.iter.Iterator;b.next=function(){goog.array.isEmpty(a)&&f();goog.asserts.assert(!goog.array.isEmpty(a));return a.shift()};return b})};goog.iter.enumerate=function(a,b){return goog.iter.zip(goog.iter.count(b),a)};\ngoog.iter.limit=function(a,b){goog.asserts.assert(goog.math.isInt(b)&&0<=b);var c=goog.iter.toIterator(a),d=new goog.iter.Iterator,e=b;d.next=function(){if(0<e--)return c.next();throw goog.iter.StopIteration;};return d};goog.iter.consume=function(a,b){goog.asserts.assert(goog.math.isInt(b)&&0<=b);for(var c=goog.iter.toIterator(a);0<b--;)goog.iter.nextOrValue(c,null);return c};\ngoog.iter.slice=function(a,b,c){goog.asserts.assert(goog.math.isInt(b)&&0<=b);a=goog.iter.consume(a,b);goog.isNumber(c)&&(goog.asserts.assert(goog.math.isInt(c)&&c>=b),a=goog.iter.limit(a,c-b));return a};goog.iter.hasDuplicates_=function(a){var b=[];goog.array.removeDuplicates(a,b);return a.length!=b.length};goog.iter.permutations=function(a,b){var c=goog.iter.toArray(a),d=goog.isNumber(b)?b:c.length,c=goog.array.repeat(c,d),c=goog.iter.product.apply(void 0,c);return goog.iter.filter(c,function(a){return!goog.iter.hasDuplicates_(a)})};\ngoog.iter.combinations=function(a,b){function c(a){return d[a]}var d=goog.iter.toArray(a),e=goog.iter.range(d.length),e=goog.iter.permutations(e,b),f=goog.iter.filter(e,function(a){return goog.array.isSorted(a)}),e=new goog.iter.Iterator;e.next=function(){return goog.array.map(f.next(),c)};return e};\ngoog.iter.combinationsWithReplacement=function(a,b){function c(a){return d[a]}var d=goog.iter.toArray(a),e=goog.array.range(d.length),e=goog.array.repeat(e,b),e=goog.iter.product.apply(void 0,e),f=goog.iter.filter(e,function(a){return goog.array.isSorted(a)}),e=new goog.iter.Iterator;e.next=function(){return goog.array.map(f.next(),c)};return e};goog.structs.Map=function(a,b){this.map_={};this.keys_=[];this.version_=this.count_=0;var c=arguments.length;if(1<c){if(c%2)throw Error(\"Uneven number of arguments\");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else a&&this.addAll(a)};goog.structs.Map.prototype.getCount=function(){return this.count_};goog.structs.Map.prototype.getValues=function(){this.cleanupKeysArray_();for(var a=[],b=0;b<this.keys_.length;b++)a.push(this.map_[this.keys_[b]]);return a};\ngoog.structs.Map.prototype.getKeys=function(){this.cleanupKeysArray_();return this.keys_.concat()};goog.structs.Map.prototype.containsKey=function(a){return goog.structs.Map.hasKey_(this.map_,a)};goog.structs.Map.prototype.containsValue=function(a){for(var b=0;b<this.keys_.length;b++){var c=this.keys_[b];if(goog.structs.Map.hasKey_(this.map_,c)&&this.map_[c]==a)return!0}return!1};\ngoog.structs.Map.prototype.equals=function(a,b){if(this===a)return!0;if(this.count_!=a.getCount())return!1;var c=b||goog.structs.Map.defaultEquals;this.cleanupKeysArray_();for(var d,e=0;d=this.keys_[e];e++)if(!c(this.get(d),a.get(d)))return!1;return!0};goog.structs.Map.defaultEquals=function(a,b){return a===b};goog.structs.Map.prototype.isEmpty=function(){return 0==this.count_};goog.structs.Map.prototype.clear=function(){this.map_={};this.version_=this.count_=this.keys_.length=0};\ngoog.structs.Map.prototype.remove=function(a){return goog.structs.Map.hasKey_(this.map_,a)?(delete this.map_[a],this.count_--,this.version_++,this.keys_.length>2*this.count_&&this.cleanupKeysArray_(),!0):!1};\ngoog.structs.Map.prototype.cleanupKeysArray_=function(){if(this.count_!=this.keys_.length){for(var a=0,b=0;a<this.keys_.length;){var c=this.keys_[a];goog.structs.Map.hasKey_(this.map_,c)&&(this.keys_[b++]=c);a++}this.keys_.length=b}if(this.count_!=this.keys_.length){for(var d={},b=a=0;a<this.keys_.length;)c=this.keys_[a],goog.structs.Map.hasKey_(d,c)||(this.keys_[b++]=c,d[c]=1),a++;this.keys_.length=b}};\ngoog.structs.Map.prototype.get=function(a,b){return goog.structs.Map.hasKey_(this.map_,a)?this.map_[a]:b};goog.structs.Map.prototype.set=function(a,b){goog.structs.Map.hasKey_(this.map_,a)||(this.count_++,this.keys_.push(a),this.version_++);this.map_[a]=b};goog.structs.Map.prototype.addAll=function(a){var b;a instanceof goog.structs.Map?(b=a.getKeys(),a=a.getValues()):(b=goog.object.getKeys(a),a=goog.object.getValues(a));for(var c=0;c<b.length;c++)this.set(b[c],a[c])};\ngoog.structs.Map.prototype.clone=function(){return new goog.structs.Map(this)};goog.structs.Map.prototype.transpose=function(){for(var a=new goog.structs.Map,b=0;b<this.keys_.length;b++){var c=this.keys_[b];a.set(this.map_[c],c)}return a};goog.structs.Map.prototype.toObject=function(){this.cleanupKeysArray_();for(var a={},b=0;b<this.keys_.length;b++){var c=this.keys_[b];a[c]=this.map_[c]}return a};goog.structs.Map.prototype.getKeyIterator=function(){return this.__iterator__(!0)};\ngoog.structs.Map.prototype.getValueIterator=function(){return this.__iterator__(!1)};goog.structs.Map.prototype.__iterator__=function(a){this.cleanupKeysArray_();var b=0,c=this.keys_,d=this.map_,e=this.version_,f=this,g=new goog.iter.Iterator;g.next=function(){for(;;){if(e!=f.version_)throw Error(\"The map has changed since the iterator was created\");if(b>=c.length)throw goog.iter.StopIteration;var g=c[b++];return a?g:d[g]}};return g};\ngoog.structs.Map.hasKey_=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};goog.structs.Set=function(a){this.map_=new goog.structs.Map;a&&this.addAll(a)};goog.structs.Set.getKey_=function(a){var b=typeof a;return\"object\"==b&&a||\"function\"==b?\"o\"+goog.getUid(a):b.substr(0,1)+a};goog.structs.Set.prototype.getCount=function(){return this.map_.getCount()};goog.structs.Set.prototype.add=function(a){this.map_.set(goog.structs.Set.getKey_(a),a)};goog.structs.Set.prototype.addAll=function(a){a=goog.structs.getValues(a);for(var b=a.length,c=0;c<b;c++)this.add(a[c])};\ngoog.structs.Set.prototype.removeAll=function(a){a=goog.structs.getValues(a);for(var b=a.length,c=0;c<b;c++)this.remove(a[c])};goog.structs.Set.prototype.remove=function(a){return this.map_.remove(goog.structs.Set.getKey_(a))};goog.structs.Set.prototype.clear=function(){this.map_.clear()};goog.structs.Set.prototype.isEmpty=function(){return this.map_.isEmpty()};goog.structs.Set.prototype.contains=function(a){return this.map_.containsKey(goog.structs.Set.getKey_(a))};\ngoog.structs.Set.prototype.containsAll=function(a){return goog.structs.every(a,this.contains,this)};goog.structs.Set.prototype.intersection=function(a){var b=new goog.structs.Set;a=goog.structs.getValues(a);for(var c=0;c<a.length;c++){var d=a[c];this.contains(d)&&b.add(d)}return b};goog.structs.Set.prototype.difference=function(a){var b=this.clone();b.removeAll(a);return b};goog.structs.Set.prototype.getValues=function(){return this.map_.getValues()};goog.structs.Set.prototype.clone=function(){return new goog.structs.Set(this)};\ngoog.structs.Set.prototype.equals=function(a){return this.getCount()==goog.structs.getCount(a)&&this.isSubsetOf(a)};goog.structs.Set.prototype.isSubsetOf=function(a){var b=goog.structs.getCount(a);if(this.getCount()>b)return!1;!(a instanceof goog.structs.Set)&&5<b&&(a=new goog.structs.Set(a));return goog.structs.every(this,function(b){return goog.structs.contains(a,b)})};goog.structs.Set.prototype.__iterator__=function(a){return this.map_.__iterator__(!1)};goog.debug.LOGGING_ENABLED=goog.DEBUG;goog.debug.catchErrors=function(a,b,c){c=c||goog.global;var d=c.onerror,e=!!b;goog.userAgent.WEBKIT&&!goog.userAgent.isVersionOrHigher(\"535.3\")&&(e=!e);c.onerror=function(b,c,h,k,l){d&&d(b,c,h,k,l);a({message:b,fileName:c,line:h,col:k,error:l});return e}};goog.debug.expose=function(a,b){if(\"undefined\"==typeof a)return\"undefined\";if(null==a)return\"NULL\";var c=[],d;for(d in a)if(b||!goog.isFunction(a[d])){var e=d+\" = \";try{e+=a[d]}catch(f){e+=\"*** \"+f+\" ***\"}c.push(e)}return c.join(\"\\n\")};\ngoog.debug.deepExpose=function(a,b){var c=new goog.structs.Set,d=[],e=function(a,g){var h=g+\"  \";try{if(goog.isDef(a))if(goog.isNull(a))d.push(\"NULL\");else if(goog.isString(a))d.push('\"'+a.replace(/\\n/g,\"\\n\"+g)+'\"');else if(goog.isFunction(a))d.push(String(a).replace(/\\n/g,\"\\n\"+g));else if(goog.isObject(a))if(c.contains(a))d.push(\"*** reference loop detected ***\");else{c.add(a);d.push(\"{\");for(var k in a)if(b||!goog.isFunction(a[k]))d.push(\"\\n\"),d.push(h),d.push(k+\" = \"),e(a[k],h);d.push(\"\\n\"+g+\"}\")}else d.push(a);\nelse d.push(\"undefined\")}catch(l){d.push(\"*** \"+l+\" ***\")}};e(a,\"\");return d.join(\"\")};goog.debug.exposeArray=function(a){for(var b=[],c=0;c<a.length;c++)goog.isArray(a[c])?b.push(goog.debug.exposeArray(a[c])):b.push(a[c]);return\"[ \"+b.join(\", \")+\" ]\"};\ngoog.debug.exposeException=function(a,b){try{var c=goog.debug.normalizeErrorObject(a);return\"Message: \"+goog.string.htmlEscape(c.message)+'\\nUrl: <a href=\"view-source:'+c.fileName+'\" target=\"_new\">'+c.fileName+\"</a>\\nLine: \"+c.lineNumber+\"\\n\\nBrowser stack:\\n\"+goog.string.htmlEscape(c.stack+\"-> \")+\"[end]\\n\\nJS stack traversal:\\n\"+goog.string.htmlEscape(goog.debug.getStacktrace(b)+\"-> \")}catch(d){return\"Exception trying to expose exception! You win, we lose. \"+d}};\ngoog.debug.normalizeErrorObject=function(a){var b=goog.getObjectByName(\"window.location.href\");if(goog.isString(a))return{message:a,name:\"Unknown error\",lineNumber:\"Not available\",fileName:b,stack:\"Not available\"};var c,d,e=!1;try{c=a.lineNumber||a.line||\"Not available\"}catch(f){c=\"Not available\",e=!0}try{d=a.fileName||a.filename||a.sourceURL||goog.global.$googDebugFname||b}catch(g){d=\"Not available\",e=!0}return!e&&a.lineNumber&&a.fileName&&a.stack&&a.message&&a.name?a:{message:a.message||\"Not available\",\nname:a.name||\"UnknownError\",lineNumber:c,fileName:d,stack:a.stack||\"Not available\"}};goog.debug.enhanceError=function(a,b){var c=\"string\"==typeof a?Error(a):a;c.stack||(c.stack=goog.debug.getStacktrace(arguments.callee.caller));if(b){for(var d=0;c[\"message\"+d];)++d;c[\"message\"+d]=String(b)}return c};\ngoog.debug.getStacktraceSimple=function(a){for(var b=[],c=arguments.callee.caller,d=0;c&&(!a||d<a);){b.push(goog.debug.getFunctionName(c));b.push(\"()\\n\");try{c=c.caller}catch(e){b.push(\"[exception trying to get caller]\\n\");break}d++;if(d>=goog.debug.MAX_STACK_DEPTH){b.push(\"[...long stack...]\");break}}a&&d>=a?b.push(\"[...reached max depth limit...]\"):b.push(\"[end]\");return b.join(\"\")};goog.debug.MAX_STACK_DEPTH=50;\ngoog.debug.getStacktrace=function(a){return goog.debug.getStacktraceHelper_(a||arguments.callee.caller,[])};\ngoog.debug.getStacktraceHelper_=function(a,b){var c=[];if(goog.array.contains(b,a))c.push(\"[...circular reference...]\");else if(a&&b.length<goog.debug.MAX_STACK_DEPTH){c.push(goog.debug.getFunctionName(a)+\"(\");for(var d=a.arguments,e=0;e<d.length;e++){0<e&&c.push(\", \");var f;f=d[e];switch(typeof f){case \"object\":f=f?\"object\":\"null\";break;case \"string\":break;case \"number\":f=String(f);break;case \"boolean\":f=f?\"true\":\"false\";break;case \"function\":f=(f=goog.debug.getFunctionName(f))?f:\"[fn]\";break;default:f=\ntypeof f}40<f.length&&(f=f.substr(0,40)+\"...\");c.push(f)}b.push(a);c.push(\")\\n\");try{c.push(goog.debug.getStacktraceHelper_(a.caller,b))}catch(g){c.push(\"[exception trying to get caller]\\n\")}}else a?c.push(\"[...long stack...]\"):c.push(\"[end]\");return c.join(\"\")};goog.debug.setFunctionResolver=function(a){goog.debug.fnNameResolver_=a};\ngoog.debug.getFunctionName=function(a){if(goog.debug.fnNameCache_[a])return goog.debug.fnNameCache_[a];if(goog.debug.fnNameResolver_){var b=goog.debug.fnNameResolver_(a);if(b)return goog.debug.fnNameCache_[a]=b}a=String(a);goog.debug.fnNameCache_[a]||(b=/function ([^\\(]+)/.exec(a),goog.debug.fnNameCache_[a]=b?b[1]:\"[Anonymous]\");return goog.debug.fnNameCache_[a]};\ngoog.debug.makeWhitespaceVisible=function(a){return a.replace(/ /g,\"[_]\").replace(/\\f/g,\"[f]\").replace(/\\n/g,\"[n]\\n\").replace(/\\r/g,\"[r]\").replace(/\\t/g,\"[t]\")};goog.debug.fnNameCache_={};goog.debug.LogRecord=function(a,b,c,d,e){this.reset(a,b,c,d,e)};goog.debug.LogRecord.prototype.sequenceNumber_=0;goog.debug.LogRecord.prototype.exception_=null;goog.debug.LogRecord.prototype.exceptionText_=null;goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS=!0;goog.debug.LogRecord.nextSequenceNumber_=0;\ngoog.debug.LogRecord.prototype.reset=function(a,b,c,d,e){goog.debug.LogRecord.ENABLE_SEQUENCE_NUMBERS&&(this.sequenceNumber_=\"number\"==typeof e?e:goog.debug.LogRecord.nextSequenceNumber_++);this.time_=d||goog.now();this.level_=a;this.msg_=b;this.loggerName_=c;delete this.exception_;delete this.exceptionText_};goog.debug.LogRecord.prototype.getLoggerName=function(){return this.loggerName_};goog.debug.LogRecord.prototype.getException=function(){return this.exception_};\ngoog.debug.LogRecord.prototype.setException=function(a){this.exception_=a};goog.debug.LogRecord.prototype.getExceptionText=function(){return this.exceptionText_};goog.debug.LogRecord.prototype.setExceptionText=function(a){this.exceptionText_=a};goog.debug.LogRecord.prototype.setLoggerName=function(a){this.loggerName_=a};goog.debug.LogRecord.prototype.getLevel=function(){return this.level_};goog.debug.LogRecord.prototype.setLevel=function(a){this.level_=a};\ngoog.debug.LogRecord.prototype.getMessage=function(){return this.msg_};goog.debug.LogRecord.prototype.setMessage=function(a){this.msg_=a};goog.debug.LogRecord.prototype.getMillis=function(){return this.time_};goog.debug.LogRecord.prototype.setMillis=function(a){this.time_=a};goog.debug.LogRecord.prototype.getSequenceNumber=function(){return this.sequenceNumber_};goog.debug.LogBuffer=function(){goog.asserts.assert(goog.debug.LogBuffer.isBufferingEnabled(),\"Cannot use goog.debug.LogBuffer without defining goog.debug.LogBuffer.CAPACITY.\");this.clear()};goog.debug.LogBuffer.getInstance=function(){goog.debug.LogBuffer.instance_||(goog.debug.LogBuffer.instance_=new goog.debug.LogBuffer);return goog.debug.LogBuffer.instance_};goog.debug.LogBuffer.CAPACITY=0;\ngoog.debug.LogBuffer.prototype.addRecord=function(a,b,c){var d=(this.curIndex_+1)%goog.debug.LogBuffer.CAPACITY;this.curIndex_=d;if(this.isFull_)return d=this.buffer_[d],d.reset(a,b,c),d;this.isFull_=d==goog.debug.LogBuffer.CAPACITY-1;return this.buffer_[d]=new goog.debug.LogRecord(a,b,c)};goog.debug.LogBuffer.isBufferingEnabled=function(){return 0<goog.debug.LogBuffer.CAPACITY};\ngoog.debug.LogBuffer.prototype.clear=function(){this.buffer_=Array(goog.debug.LogBuffer.CAPACITY);this.curIndex_=-1;this.isFull_=!1};goog.debug.LogBuffer.prototype.forEachRecord=function(a){var b=this.buffer_;if(b[0]){var c=this.curIndex_,d=this.isFull_?c:-1;do d=(d+1)%goog.debug.LogBuffer.CAPACITY,a(b[d]);while(d!=c)}};goog.debug.Logger=function(a){this.name_=a};goog.debug.Logger.prototype.parent_=null;goog.debug.Logger.prototype.level_=null;goog.debug.Logger.prototype.children_=null;goog.debug.Logger.prototype.handlers_=null;goog.debug.Logger.ENABLE_HIERARCHY=!0;goog.debug.Logger.ENABLE_HIERARCHY||(goog.debug.Logger.rootHandlers_=[]);goog.debug.Logger.Level=function(a,b){this.name=a;this.value=b};goog.debug.Logger.Level.prototype.toString=function(){return this.name};\ngoog.debug.Logger.Level.OFF=new goog.debug.Logger.Level(\"OFF\",Infinity);goog.debug.Logger.Level.SHOUT=new goog.debug.Logger.Level(\"SHOUT\",1200);goog.debug.Logger.Level.SEVERE=new goog.debug.Logger.Level(\"SEVERE\",1E3);goog.debug.Logger.Level.WARNING=new goog.debug.Logger.Level(\"WARNING\",900);goog.debug.Logger.Level.INFO=new goog.debug.Logger.Level(\"INFO\",800);goog.debug.Logger.Level.CONFIG=new goog.debug.Logger.Level(\"CONFIG\",700);goog.debug.Logger.Level.FINE=new goog.debug.Logger.Level(\"FINE\",500);\ngoog.debug.Logger.Level.FINER=new goog.debug.Logger.Level(\"FINER\",400);goog.debug.Logger.Level.FINEST=new goog.debug.Logger.Level(\"FINEST\",300);goog.debug.Logger.Level.ALL=new goog.debug.Logger.Level(\"ALL\",0);\ngoog.debug.Logger.Level.PREDEFINED_LEVELS=[goog.debug.Logger.Level.OFF,goog.debug.Logger.Level.SHOUT,goog.debug.Logger.Level.SEVERE,goog.debug.Logger.Level.WARNING,goog.debug.Logger.Level.INFO,goog.debug.Logger.Level.CONFIG,goog.debug.Logger.Level.FINE,goog.debug.Logger.Level.FINER,goog.debug.Logger.Level.FINEST,goog.debug.Logger.Level.ALL];goog.debug.Logger.Level.predefinedLevelsCache_=null;\ngoog.debug.Logger.Level.createPredefinedLevelsCache_=function(){goog.debug.Logger.Level.predefinedLevelsCache_={};for(var a=0,b;b=goog.debug.Logger.Level.PREDEFINED_LEVELS[a];a++)goog.debug.Logger.Level.predefinedLevelsCache_[b.value]=b,goog.debug.Logger.Level.predefinedLevelsCache_[b.name]=b};\ngoog.debug.Logger.Level.getPredefinedLevel=function(a){goog.debug.Logger.Level.predefinedLevelsCache_||goog.debug.Logger.Level.createPredefinedLevelsCache_();return goog.debug.Logger.Level.predefinedLevelsCache_[a]||null};\ngoog.debug.Logger.Level.getPredefinedLevelByValue=function(a){goog.debug.Logger.Level.predefinedLevelsCache_||goog.debug.Logger.Level.createPredefinedLevelsCache_();if(a in goog.debug.Logger.Level.predefinedLevelsCache_)return goog.debug.Logger.Level.predefinedLevelsCache_[a];for(var b=0;b<goog.debug.Logger.Level.PREDEFINED_LEVELS.length;++b){var c=goog.debug.Logger.Level.PREDEFINED_LEVELS[b];if(c.value<=a)return c}return null};goog.debug.Logger.getLogger=function(a){return goog.debug.LogManager.getLogger(a)};\ngoog.debug.Logger.logToProfilers=function(a){goog.global.console&&(goog.global.console.timeStamp?goog.global.console.timeStamp(a):goog.global.console.markTimeline&&goog.global.console.markTimeline(a));goog.global.msWriteProfilerMark&&goog.global.msWriteProfilerMark(a)};goog.debug.Logger.prototype.getName=function(){return this.name_};\ngoog.debug.Logger.prototype.addHandler=function(a){goog.debug.LOGGING_ENABLED&&(goog.debug.Logger.ENABLE_HIERARCHY?(this.handlers_||(this.handlers_=[]),this.handlers_.push(a)):(goog.asserts.assert(!this.name_,\"Cannot call addHandler on a non-root logger when goog.debug.Logger.ENABLE_HIERARCHY is false.\"),goog.debug.Logger.rootHandlers_.push(a)))};\ngoog.debug.Logger.prototype.removeHandler=function(a){if(goog.debug.LOGGING_ENABLED){var b=goog.debug.Logger.ENABLE_HIERARCHY?this.handlers_:goog.debug.Logger.rootHandlers_;return!!b&&goog.array.remove(b,a)}return!1};goog.debug.Logger.prototype.getParent=function(){return this.parent_};goog.debug.Logger.prototype.getChildren=function(){this.children_||(this.children_={});return this.children_};\ngoog.debug.Logger.prototype.setLevel=function(a){goog.debug.LOGGING_ENABLED&&(goog.debug.Logger.ENABLE_HIERARCHY?this.level_=a:(goog.asserts.assert(!this.name_,\"Cannot call setLevel() on a non-root logger when goog.debug.Logger.ENABLE_HIERARCHY is false.\"),goog.debug.Logger.rootLevel_=a))};goog.debug.Logger.prototype.getLevel=function(){return goog.debug.LOGGING_ENABLED?this.level_:goog.debug.Logger.Level.OFF};\ngoog.debug.Logger.prototype.getEffectiveLevel=function(){if(!goog.debug.LOGGING_ENABLED)return goog.debug.Logger.Level.OFF;if(!goog.debug.Logger.ENABLE_HIERARCHY)return goog.debug.Logger.rootLevel_;if(this.level_)return this.level_;if(this.parent_)return this.parent_.getEffectiveLevel();goog.asserts.fail(\"Root logger has no level set.\");return null};goog.debug.Logger.prototype.isLoggable=function(a){return goog.debug.LOGGING_ENABLED&&a.value>=this.getEffectiveLevel().value};\ngoog.debug.Logger.prototype.log=function(a,b,c){goog.debug.LOGGING_ENABLED&&this.isLoggable(a)&&this.doLogRecord_(this.getLogRecord(a,b,c))};goog.debug.Logger.prototype.getLogRecord=function(a,b,c){var d=goog.debug.LogBuffer.isBufferingEnabled()?goog.debug.LogBuffer.getInstance().addRecord(a,b,this.name_):new goog.debug.LogRecord(a,String(b),this.name_);c&&(d.setException(c),d.setExceptionText(goog.debug.exposeException(c,arguments.callee.caller)));return d};\ngoog.debug.Logger.prototype.shout=function(a,b){goog.debug.LOGGING_ENABLED&&this.log(goog.debug.Logger.Level.SHOUT,a,b)};goog.debug.Logger.prototype.severe=function(a,b){goog.debug.LOGGING_ENABLED&&this.log(goog.debug.Logger.Level.SEVERE,a,b)};goog.debug.Logger.prototype.warning=function(a,b){goog.debug.LOGGING_ENABLED&&this.log(goog.debug.Logger.Level.WARNING,a,b)};goog.debug.Logger.prototype.info=function(a,b){goog.debug.LOGGING_ENABLED&&this.log(goog.debug.Logger.Level.INFO,a,b)};\ngoog.debug.Logger.prototype.config=function(a,b){goog.debug.LOGGING_ENABLED&&this.log(goog.debug.Logger.Level.CONFIG,a,b)};goog.debug.Logger.prototype.fine=function(a,b){goog.debug.LOGGING_ENABLED&&this.log(goog.debug.Logger.Level.FINE,a,b)};goog.debug.Logger.prototype.finer=function(a,b){goog.debug.LOGGING_ENABLED&&this.log(goog.debug.Logger.Level.FINER,a,b)};goog.debug.Logger.prototype.finest=function(a,b){goog.debug.LOGGING_ENABLED&&this.log(goog.debug.Logger.Level.FINEST,a,b)};\ngoog.debug.Logger.prototype.logRecord=function(a){goog.debug.LOGGING_ENABLED&&this.isLoggable(a.getLevel())&&this.doLogRecord_(a)};goog.debug.Logger.prototype.doLogRecord_=function(a){goog.debug.Logger.logToProfilers(\"log:\"+a.getMessage());if(goog.debug.Logger.ENABLE_HIERARCHY)for(var b=this;b;)b.callPublish_(a),b=b.getParent();else for(var b=0,c;c=goog.debug.Logger.rootHandlers_[b++];)c(a)};goog.debug.Logger.prototype.callPublish_=function(a){if(this.handlers_)for(var b=0,c;c=this.handlers_[b];b++)c(a)};\ngoog.debug.Logger.prototype.setParent_=function(a){this.parent_=a};goog.debug.Logger.prototype.addChild_=function(a,b){this.getChildren()[a]=b};goog.debug.LogManager={};goog.debug.LogManager.loggers_={};goog.debug.LogManager.rootLogger_=null;goog.debug.LogManager.initialize=function(){goog.debug.LogManager.rootLogger_||(goog.debug.LogManager.rootLogger_=new goog.debug.Logger(\"\"),goog.debug.LogManager.loggers_[\"\"]=goog.debug.LogManager.rootLogger_,goog.debug.LogManager.rootLogger_.setLevel(goog.debug.Logger.Level.CONFIG))};\ngoog.debug.LogManager.getLoggers=function(){return goog.debug.LogManager.loggers_};goog.debug.LogManager.getRoot=function(){goog.debug.LogManager.initialize();return goog.debug.LogManager.rootLogger_};goog.debug.LogManager.getLogger=function(a){goog.debug.LogManager.initialize();return goog.debug.LogManager.loggers_[a]||goog.debug.LogManager.createLogger_(a)};\ngoog.debug.LogManager.createFunctionForCatchErrors=function(a){return function(b){(a||goog.debug.LogManager.getRoot()).severe(\"Error: \"+b.message+\" (\"+b.fileName+\" @ Line: \"+b.line+\")\")}};goog.debug.LogManager.createLogger_=function(a){var b=new goog.debug.Logger(a);if(goog.debug.Logger.ENABLE_HIERARCHY){var c=a.lastIndexOf(\".\"),d=a.substr(0,c),c=a.substr(c+1),d=goog.debug.LogManager.getLogger(d);d.addChild_(c,b);b.setParent_(d)}return goog.debug.LogManager.loggers_[a]=b};goog.log={};goog.log.ENABLED=goog.debug.LOGGING_ENABLED;goog.log.Logger=goog.debug.Logger;goog.log.Level=goog.debug.Logger.Level;goog.log.LogRecord=goog.debug.LogRecord;goog.log.getLogger=function(a,b){if(goog.log.ENABLED){var c=goog.debug.Logger.getLogger(a);b&&c&&c.setLevel(b);return c}return null};goog.log.addHandler=function(a,b){goog.log.ENABLED&&a&&a.addHandler(b)};goog.log.removeHandler=function(a,b){return goog.log.ENABLED&&a?a.removeHandler(b):!1};\ngoog.log.log=function(a,b,c,d){goog.log.ENABLED&&a&&a.log(b,c,d)};goog.log.error=function(a,b,c){goog.log.ENABLED&&a&&a.severe(b,c)};goog.log.warning=function(a,b,c){goog.log.ENABLED&&a&&a.warning(b,c)};goog.log.info=function(a,b,c){goog.log.ENABLED&&a&&a.info(b,c)};goog.log.fine=function(a,b,c){goog.log.ENABLED&&a&&a.fine(b,c)};goog.net={};goog.net.ErrorCode={NO_ERROR:0,ACCESS_DENIED:1,FILE_NOT_FOUND:2,FF_SILENT_ERROR:3,CUSTOM_ERROR:4,EXCEPTION:5,HTTP_ERROR:6,ABORT:7,TIMEOUT:8,OFFLINE:9};\ngoog.net.ErrorCode.getDebugMessage=function(a){switch(a){case goog.net.ErrorCode.NO_ERROR:return\"No Error\";case goog.net.ErrorCode.ACCESS_DENIED:return\"Access denied to content document\";case goog.net.ErrorCode.FILE_NOT_FOUND:return\"File not found\";case goog.net.ErrorCode.FF_SILENT_ERROR:return\"Firefox silently errored\";case goog.net.ErrorCode.CUSTOM_ERROR:return\"Application custom error\";case goog.net.ErrorCode.EXCEPTION:return\"An exception occurred\";case goog.net.ErrorCode.HTTP_ERROR:return\"Http response at 400 or 500 level\";\ncase goog.net.ErrorCode.ABORT:return\"Request was aborted\";case goog.net.ErrorCode.TIMEOUT:return\"Request timed out\";case goog.net.ErrorCode.OFFLINE:return\"The resource is not available offline\";default:return\"Unrecognized error code\"}};goog.net.EventType={COMPLETE:\"complete\",SUCCESS:\"success\",ERROR:\"error\",ABORT:\"abort\",READY:\"ready\",READY_STATE_CHANGE:\"readystatechange\",TIMEOUT:\"timeout\",INCREMENTAL_DATA:\"incrementaldata\",PROGRESS:\"progress\"};goog.net.HttpStatus={CONTINUE:100,SWITCHING_PROTOCOLS:101,OK:200,CREATED:201,ACCEPTED:202,NON_AUTHORITATIVE_INFORMATION:203,NO_CONTENT:204,RESET_CONTENT:205,PARTIAL_CONTENT:206,MULTIPLE_CHOICES:300,MOVED_PERMANENTLY:301,FOUND:302,SEE_OTHER:303,NOT_MODIFIED:304,USE_PROXY:305,TEMPORARY_REDIRECT:307,BAD_REQUEST:400,UNAUTHORIZED:401,PAYMENT_REQUIRED:402,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_ALLOWED:405,NOT_ACCEPTABLE:406,PROXY_AUTHENTICATION_REQUIRED:407,REQUEST_TIMEOUT:408,CONFLICT:409,GONE:410,LENGTH_REQUIRED:411,\nPRECONDITION_FAILED:412,REQUEST_ENTITY_TOO_LARGE:413,REQUEST_URI_TOO_LONG:414,UNSUPPORTED_MEDIA_TYPE:415,REQUEST_RANGE_NOT_SATISFIABLE:416,EXPECTATION_FAILED:417,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504,HTTP_VERSION_NOT_SUPPORTED:505,QUIRK_IE_NO_CONTENT:1223};\ngoog.net.HttpStatus.isSuccess=function(a){switch(a){case goog.net.HttpStatus.OK:case goog.net.HttpStatus.CREATED:case goog.net.HttpStatus.ACCEPTED:case goog.net.HttpStatus.NO_CONTENT:case goog.net.HttpStatus.PARTIAL_CONTENT:case goog.net.HttpStatus.NOT_MODIFIED:case goog.net.HttpStatus.QUIRK_IE_NO_CONTENT:return!0;default:return!1}};goog.net.XmlHttpFactory=function(){};goog.net.XmlHttpFactory.prototype.cachedOptions_=null;goog.net.XmlHttpFactory.prototype.getOptions=function(){return this.cachedOptions_||(this.cachedOptions_=this.internalGetOptions())};goog.net.WrapperXmlHttpFactory=function(a,b){goog.net.XmlHttpFactory.call(this);this.xhrFactory_=a;this.optionsFactory_=b};goog.inherits(goog.net.WrapperXmlHttpFactory,goog.net.XmlHttpFactory);goog.net.WrapperXmlHttpFactory.prototype.createInstance=function(){return this.xhrFactory_()};goog.net.WrapperXmlHttpFactory.prototype.getOptions=function(){return this.optionsFactory_()};goog.net.XmlHttp=function(){return goog.net.XmlHttp.factory_.createInstance()};goog.net.XmlHttp.ASSUME_NATIVE_XHR=!1;goog.net.XmlHttp.getOptions=function(){return goog.net.XmlHttp.factory_.getOptions()};goog.net.XmlHttp.OptionType={USE_NULL_FUNCTION:0,LOCAL_REQUEST_ERROR:1};goog.net.XmlHttp.ReadyState={UNINITIALIZED:0,LOADING:1,LOADED:2,INTERACTIVE:3,COMPLETE:4};goog.net.XmlHttp.setFactory=function(a,b){goog.net.XmlHttp.setGlobalFactory(new goog.net.WrapperXmlHttpFactory(a,b))};\ngoog.net.XmlHttp.setGlobalFactory=function(a){goog.net.XmlHttp.factory_=a};goog.net.DefaultXmlHttpFactory=function(){goog.net.XmlHttpFactory.call(this)};goog.inherits(goog.net.DefaultXmlHttpFactory,goog.net.XmlHttpFactory);goog.net.DefaultXmlHttpFactory.prototype.createInstance=function(){var a=this.getProgId_();return a?new ActiveXObject(a):new XMLHttpRequest};\ngoog.net.DefaultXmlHttpFactory.prototype.internalGetOptions=function(){var a={};this.getProgId_()&&(a[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION]=!0,a[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR]=!0);return a};\ngoog.net.DefaultXmlHttpFactory.prototype.getProgId_=function(){if(goog.net.XmlHttp.ASSUME_NATIVE_XHR)return\"\";if(!this.ieProgId_&&\"undefined\"==typeof XMLHttpRequest&&\"undefined\"!=typeof ActiveXObject){for(var a=[\"MSXML2.XMLHTTP.6.0\",\"MSXML2.XMLHTTP.3.0\",\"MSXML2.XMLHTTP\",\"Microsoft.XMLHTTP\"],b=0;b<a.length;b++){var c=a[b];try{return new ActiveXObject(c),this.ieProgId_=c}catch(d){}}throw Error(\"Could not create ActiveXObject. ActiveX might be disabled, or MSXML might not be installed\");}return this.ieProgId_};\ngoog.net.XmlHttp.setGlobalFactory(new goog.net.DefaultXmlHttpFactory);goog.uri={};goog.uri.utils={};goog.uri.utils.CharCode_={AMPERSAND:38,EQUAL:61,HASH:35,QUESTION:63};goog.uri.utils.buildFromEncodedParts=function(a,b,c,d,e,f,g){var h=\"\";a&&(h+=a+\":\");c&&(h+=\"//\",b&&(h+=b+\"@\"),h+=c,d&&(h+=\":\"+d));e&&(h+=e);f&&(h+=\"?\"+f);g&&(h+=\"#\"+g);return h};goog.uri.utils.splitRe_=RegExp(\"^(?:([^:/?#.]+):)?(?://(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\\\\?([^#]*))?(?:#(.*))?$\");\ngoog.uri.utils.ComponentIndex={SCHEME:1,USER_INFO:2,DOMAIN:3,PORT:4,PATH:5,QUERY_DATA:6,FRAGMENT:7};goog.uri.utils.split=function(a){goog.uri.utils.phishingProtection_();return a.match(goog.uri.utils.splitRe_)};goog.uri.utils.needsPhishingProtection_=goog.userAgent.WEBKIT;\ngoog.uri.utils.phishingProtection_=function(){if(goog.uri.utils.needsPhishingProtection_){goog.uri.utils.needsPhishingProtection_=!1;var a=goog.global.location;if(a){var b=a.href;if(b&&(b=goog.uri.utils.getDomain(b))&&b!=a.hostname)throw goog.uri.utils.needsPhishingProtection_=!0,Error();}}};goog.uri.utils.decodeIfPossible_=function(a){return a&&decodeURIComponent(a)};goog.uri.utils.getComponentByIndex_=function(a,b){return goog.uri.utils.split(b)[a]||null};\ngoog.uri.utils.getScheme=function(a){return goog.uri.utils.getComponentByIndex_(goog.uri.utils.ComponentIndex.SCHEME,a)};goog.uri.utils.getEffectiveScheme=function(a){a=goog.uri.utils.getScheme(a);!a&&self.location&&(a=self.location.protocol,a=a.substr(0,a.length-1));return a?a.toLowerCase():\"\"};goog.uri.utils.getUserInfoEncoded=function(a){return goog.uri.utils.getComponentByIndex_(goog.uri.utils.ComponentIndex.USER_INFO,a)};goog.uri.utils.getUserInfo=function(a){return goog.uri.utils.decodeIfPossible_(goog.uri.utils.getUserInfoEncoded(a))};\ngoog.uri.utils.getDomainEncoded=function(a){return goog.uri.utils.getComponentByIndex_(goog.uri.utils.ComponentIndex.DOMAIN,a)};goog.uri.utils.getDomain=function(a){return goog.uri.utils.decodeIfPossible_(goog.uri.utils.getDomainEncoded(a))};goog.uri.utils.getPort=function(a){return Number(goog.uri.utils.getComponentByIndex_(goog.uri.utils.ComponentIndex.PORT,a))||null};goog.uri.utils.getPathEncoded=function(a){return goog.uri.utils.getComponentByIndex_(goog.uri.utils.ComponentIndex.PATH,a)};\ngoog.uri.utils.getPath=function(a){return goog.uri.utils.decodeIfPossible_(goog.uri.utils.getPathEncoded(a))};goog.uri.utils.getQueryData=function(a){return goog.uri.utils.getComponentByIndex_(goog.uri.utils.ComponentIndex.QUERY_DATA,a)};goog.uri.utils.getFragmentEncoded=function(a){var b=a.indexOf(\"#\");return 0>b?null:a.substr(b+1)};goog.uri.utils.setFragmentEncoded=function(a,b){return goog.uri.utils.removeFragment(a)+(b?\"#\"+b:\"\")};goog.uri.utils.getFragment=function(a){return goog.uri.utils.decodeIfPossible_(goog.uri.utils.getFragmentEncoded(a))};\ngoog.uri.utils.getHost=function(a){a=goog.uri.utils.split(a);return goog.uri.utils.buildFromEncodedParts(a[goog.uri.utils.ComponentIndex.SCHEME],a[goog.uri.utils.ComponentIndex.USER_INFO],a[goog.uri.utils.ComponentIndex.DOMAIN],a[goog.uri.utils.ComponentIndex.PORT])};goog.uri.utils.getPathAndAfter=function(a){a=goog.uri.utils.split(a);return goog.uri.utils.buildFromEncodedParts(null,null,null,null,a[goog.uri.utils.ComponentIndex.PATH],a[goog.uri.utils.ComponentIndex.QUERY_DATA],a[goog.uri.utils.ComponentIndex.FRAGMENT])};\ngoog.uri.utils.removeFragment=function(a){var b=a.indexOf(\"#\");return 0>b?a:a.substr(0,b)};goog.uri.utils.haveSameDomain=function(a,b){var c=goog.uri.utils.split(a),d=goog.uri.utils.split(b);return c[goog.uri.utils.ComponentIndex.DOMAIN]==d[goog.uri.utils.ComponentIndex.DOMAIN]&&c[goog.uri.utils.ComponentIndex.SCHEME]==d[goog.uri.utils.ComponentIndex.SCHEME]&&c[goog.uri.utils.ComponentIndex.PORT]==d[goog.uri.utils.ComponentIndex.PORT]};\ngoog.uri.utils.assertNoFragmentsOrQueries_=function(a){if(goog.DEBUG&&(0<=a.indexOf(\"#\")||0<=a.indexOf(\"?\")))throw Error(\"goog.uri.utils: Fragment or query identifiers are not supported: [\"+a+\"]\");};goog.uri.utils.appendQueryData_=function(a){if(a[1]){var b=a[0],c=b.indexOf(\"#\");0<=c&&(a.push(b.substr(c)),a[0]=b=b.substr(0,c));c=b.indexOf(\"?\");0>c?a[1]=\"?\":c==b.length-1&&(a[1]=void 0)}return a.join(\"\")};\ngoog.uri.utils.appendKeyValuePairs_=function(a,b,c){if(goog.isArray(b)){goog.asserts.assertArray(b);for(var d=0;d<b.length;d++)goog.uri.utils.appendKeyValuePairs_(a,String(b[d]),c)}else null!=b&&c.push(\"&\",a,\"\"===b?\"\":\"=\",goog.string.urlEncode(b))};goog.uri.utils.buildQueryDataBuffer_=function(a,b,c){goog.asserts.assert(0==Math.max(b.length-(c||0),0)%2,\"goog.uri.utils: Key/value lists must be even in length.\");for(c=c||0;c<b.length;c+=2)goog.uri.utils.appendKeyValuePairs_(b[c],b[c+1],a);return a};\ngoog.uri.utils.buildQueryData=function(a,b){var c=goog.uri.utils.buildQueryDataBuffer_([],a,b);c[0]=\"\";return c.join(\"\")};goog.uri.utils.buildQueryDataBufferFromMap_=function(a,b){for(var c in b)goog.uri.utils.appendKeyValuePairs_(c,b[c],a);return a};goog.uri.utils.buildQueryDataFromMap=function(a){a=goog.uri.utils.buildQueryDataBufferFromMap_([],a);a[0]=\"\";return a.join(\"\")};\ngoog.uri.utils.appendParams=function(a,b){return goog.uri.utils.appendQueryData_(2==arguments.length?goog.uri.utils.buildQueryDataBuffer_([a],arguments[1],0):goog.uri.utils.buildQueryDataBuffer_([a],arguments,1))};goog.uri.utils.appendParamsFromMap=function(a,b){return goog.uri.utils.appendQueryData_(goog.uri.utils.buildQueryDataBufferFromMap_([a],b))};goog.uri.utils.appendParam=function(a,b,c){a=[a,\"&\",b];goog.isDefAndNotNull(c)&&a.push(\"=\",goog.string.urlEncode(c));return goog.uri.utils.appendQueryData_(a)};\ngoog.uri.utils.findParam_=function(a,b,c,d){for(var e=c.length;0<=(b=a.indexOf(c,b))&&b<d;){var f=a.charCodeAt(b-1);if(f==goog.uri.utils.CharCode_.AMPERSAND||f==goog.uri.utils.CharCode_.QUESTION)if(f=a.charCodeAt(b+e),!f||f==goog.uri.utils.CharCode_.EQUAL||f==goog.uri.utils.CharCode_.AMPERSAND||f==goog.uri.utils.CharCode_.HASH)return b;b+=e+1}return-1};goog.uri.utils.hashOrEndRe_=/#|$/;goog.uri.utils.hasParam=function(a,b){return 0<=goog.uri.utils.findParam_(a,0,b,a.search(goog.uri.utils.hashOrEndRe_))};\ngoog.uri.utils.getParamValue=function(a,b){var c=a.search(goog.uri.utils.hashOrEndRe_),d=goog.uri.utils.findParam_(a,0,b,c);if(0>d)return null;var e=a.indexOf(\"&\",d);if(0>e||e>c)e=c;d+=b.length+1;return goog.string.urlDecode(a.substr(d,e-d))};goog.uri.utils.getParamValues=function(a,b){for(var c=a.search(goog.uri.utils.hashOrEndRe_),d=0,e,f=[];0<=(e=goog.uri.utils.findParam_(a,d,b,c));){d=a.indexOf(\"&\",e);if(0>d||d>c)d=c;e+=b.length+1;f.push(goog.string.urlDecode(a.substr(e,d-e)))}return f};\ngoog.uri.utils.trailingQueryPunctuationRe_=/[?&]($|#)/;goog.uri.utils.removeParam=function(a,b){for(var c=a.search(goog.uri.utils.hashOrEndRe_),d=0,e,f=[];0<=(e=goog.uri.utils.findParam_(a,d,b,c));)f.push(a.substring(d,e)),d=Math.min(a.indexOf(\"&\",e)+1||c,c);f.push(a.substr(d));return f.join(\"\").replace(goog.uri.utils.trailingQueryPunctuationRe_,\"$1\")};goog.uri.utils.setParam=function(a,b,c){return goog.uri.utils.appendParam(goog.uri.utils.removeParam(a,b),b,c)};\ngoog.uri.utils.appendPath=function(a,b){goog.uri.utils.assertNoFragmentsOrQueries_(a);goog.string.endsWith(a,\"/\")&&(a=a.substr(0,a.length-1));goog.string.startsWith(b,\"/\")&&(b=b.substr(1));return goog.string.buildString(a,\"/\",b)};goog.uri.utils.StandardQueryParam={RANDOM:\"zx\"};goog.uri.utils.makeUnique=function(a){return goog.uri.utils.setParam(a,goog.uri.utils.StandardQueryParam.RANDOM,goog.string.getRandomString())};goog.net.XhrIo=function(a){goog.events.EventTarget.call(this);this.headers=new goog.structs.Map;this.xmlHttpFactory_=a||null;this.active_=!1;this.xhrOptions_=this.xhr_=null;this.lastMethod_=this.lastUri_=\"\";this.lastErrorCode_=goog.net.ErrorCode.NO_ERROR;this.lastError_=\"\";this.inAbort_=this.inOpen_=this.inSend_=this.errorDispatched_=!1;this.timeoutInterval_=0;this.timeoutId_=null;this.responseType_=goog.net.XhrIo.ResponseType.DEFAULT;this.useXhr2Timeout_=this.withCredentials_=!1};\ngoog.inherits(goog.net.XhrIo,goog.events.EventTarget);goog.net.XhrIo.ResponseType={DEFAULT:\"\",TEXT:\"text\",DOCUMENT:\"document\",BLOB:\"blob\",ARRAY_BUFFER:\"arraybuffer\"};goog.net.XhrIo.prototype.logger_=goog.log.getLogger(\"goog.net.XhrIo\");goog.net.XhrIo.CONTENT_TYPE_HEADER=\"Content-Type\";goog.net.XhrIo.HTTP_SCHEME_PATTERN=/^https?$/i;goog.net.XhrIo.METHODS_WITH_FORM_DATA=[\"POST\",\"PUT\"];goog.net.XhrIo.FORM_CONTENT_TYPE=\"application/x-www-form-urlencoded;charset=utf-8\";goog.net.XhrIo.XHR2_TIMEOUT_=\"timeout\";\ngoog.net.XhrIo.XHR2_ON_TIMEOUT_=\"ontimeout\";goog.net.XhrIo.sendInstances_=[];goog.net.XhrIo.send=function(a,b,c,d,e,f,g){var h=new goog.net.XhrIo;goog.net.XhrIo.sendInstances_.push(h);b&&h.listen(goog.net.EventType.COMPLETE,b);h.listenOnce(goog.net.EventType.READY,h.cleanupSend_);f&&h.setTimeoutInterval(f);g&&h.setWithCredentials(g);h.send(a,c,d,e)};goog.net.XhrIo.cleanup=function(){for(var a=goog.net.XhrIo.sendInstances_;a.length;)a.pop().dispose()};\ngoog.net.XhrIo.protectEntryPoints=function(a){goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_=a.protectEntryPoint(goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_)};goog.net.XhrIo.prototype.cleanupSend_=function(){this.dispose();goog.array.remove(goog.net.XhrIo.sendInstances_,this)};goog.net.XhrIo.prototype.getTimeoutInterval=function(){return this.timeoutInterval_};goog.net.XhrIo.prototype.setTimeoutInterval=function(a){this.timeoutInterval_=Math.max(0,a)};\ngoog.net.XhrIo.prototype.setResponseType=function(a){this.responseType_=a};goog.net.XhrIo.prototype.getResponseType=function(){return this.responseType_};goog.net.XhrIo.prototype.setWithCredentials=function(a){this.withCredentials_=a};goog.net.XhrIo.prototype.getWithCredentials=function(){return this.withCredentials_};\ngoog.net.XhrIo.prototype.send=function(a,b,c,d){if(this.xhr_)throw Error(\"[goog.net.XhrIo] Object is active with another request=\"+this.lastUri_+\"; newUri=\"+a);b=b?b.toUpperCase():\"GET\";this.lastUri_=a;this.lastError_=\"\";this.lastErrorCode_=goog.net.ErrorCode.NO_ERROR;this.lastMethod_=b;this.errorDispatched_=!1;this.active_=!0;this.xhr_=this.createXhr();this.xhrOptions_=this.xmlHttpFactory_?this.xmlHttpFactory_.getOptions():goog.net.XmlHttp.getOptions();this.xhr_.onreadystatechange=goog.bind(this.onReadyStateChange_,\nthis);try{goog.log.fine(this.logger_,this.formatMsg_(\"Opening Xhr\")),this.inOpen_=!0,this.xhr_.open(b,a,!0),this.inOpen_=!1}catch(e){goog.log.fine(this.logger_,this.formatMsg_(\"Error opening Xhr: \"+e.message));this.error_(goog.net.ErrorCode.EXCEPTION,e);return}a=c||\"\";var f=this.headers.clone();d&&goog.structs.forEach(d,function(a,b){f.set(b,a)});d=goog.array.find(f.getKeys(),goog.net.XhrIo.isContentTypeHeader_);c=goog.global.FormData&&a instanceof goog.global.FormData;!goog.array.contains(goog.net.XhrIo.METHODS_WITH_FORM_DATA,\nb)||d||c||f.set(goog.net.XhrIo.CONTENT_TYPE_HEADER,goog.net.XhrIo.FORM_CONTENT_TYPE);goog.structs.forEach(f,function(a,b){this.xhr_.setRequestHeader(b,a)},this);this.responseType_&&(this.xhr_.responseType=this.responseType_);goog.object.containsKey(this.xhr_,\"withCredentials\")&&(this.xhr_.withCredentials=this.withCredentials_);try{this.cleanUpTimeoutTimer_(),0<this.timeoutInterval_&&(this.useXhr2Timeout_=goog.net.XhrIo.shouldUseXhr2Timeout_(this.xhr_),goog.log.fine(this.logger_,this.formatMsg_(\"Will abort after \"+\nthis.timeoutInterval_+\"ms if incomplete, xhr2 \"+this.useXhr2Timeout_)),this.useXhr2Timeout_?(this.xhr_[goog.net.XhrIo.XHR2_TIMEOUT_]=this.timeoutInterval_,this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_]=goog.bind(this.timeout_,this)):this.timeoutId_=goog.Timer.callOnce(this.timeout_,this.timeoutInterval_,this)),goog.log.fine(this.logger_,this.formatMsg_(\"Sending request\")),this.inSend_=!0,this.xhr_.send(a),this.inSend_=!1}catch(g){goog.log.fine(this.logger_,this.formatMsg_(\"Send error: \"+g.message)),this.error_(goog.net.ErrorCode.EXCEPTION,\ng)}};goog.net.XhrIo.shouldUseXhr2Timeout_=function(a){return goog.userAgent.IE&&goog.userAgent.isVersionOrHigher(9)&&goog.isNumber(a[goog.net.XhrIo.XHR2_TIMEOUT_])&&goog.isDef(a[goog.net.XhrIo.XHR2_ON_TIMEOUT_])};goog.net.XhrIo.isContentTypeHeader_=function(a){return goog.string.caseInsensitiveEquals(goog.net.XhrIo.CONTENT_TYPE_HEADER,a)};goog.net.XhrIo.prototype.createXhr=function(){return this.xmlHttpFactory_?this.xmlHttpFactory_.createInstance():goog.net.XmlHttp()};\ngoog.net.XhrIo.prototype.timeout_=function(){\"undefined\"!=typeof goog&&this.xhr_&&(this.lastError_=\"Timed out after \"+this.timeoutInterval_+\"ms, aborting\",this.lastErrorCode_=goog.net.ErrorCode.TIMEOUT,goog.log.fine(this.logger_,this.formatMsg_(this.lastError_)),this.dispatchEvent(goog.net.EventType.TIMEOUT),this.abort(goog.net.ErrorCode.TIMEOUT))};\ngoog.net.XhrIo.prototype.error_=function(a,b){this.active_=!1;this.xhr_&&(this.inAbort_=!0,this.xhr_.abort(),this.inAbort_=!1);this.lastError_=b;this.lastErrorCode_=a;this.dispatchErrors_();this.cleanUpXhr_()};goog.net.XhrIo.prototype.dispatchErrors_=function(){this.errorDispatched_||(this.errorDispatched_=!0,this.dispatchEvent(goog.net.EventType.COMPLETE),this.dispatchEvent(goog.net.EventType.ERROR))};\ngoog.net.XhrIo.prototype.abort=function(a){this.xhr_&&this.active_&&(goog.log.fine(this.logger_,this.formatMsg_(\"Aborting\")),this.active_=!1,this.inAbort_=!0,this.xhr_.abort(),this.inAbort_=!1,this.lastErrorCode_=a||goog.net.ErrorCode.ABORT,this.dispatchEvent(goog.net.EventType.COMPLETE),this.dispatchEvent(goog.net.EventType.ABORT),this.cleanUpXhr_())};\ngoog.net.XhrIo.prototype.disposeInternal=function(){this.xhr_&&(this.active_&&(this.active_=!1,this.inAbort_=!0,this.xhr_.abort(),this.inAbort_=!1),this.cleanUpXhr_(!0));goog.net.XhrIo.superClass_.disposeInternal.call(this)};goog.net.XhrIo.prototype.onReadyStateChange_=function(){if(!this.isDisposed())if(this.inOpen_||this.inSend_||this.inAbort_)this.onReadyStateChangeHelper_();else this.onReadyStateChangeEntryPoint_()};goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_=function(){this.onReadyStateChangeHelper_()};\ngoog.net.XhrIo.prototype.onReadyStateChangeHelper_=function(){if(this.active_&&\"undefined\"!=typeof goog)if(this.xhrOptions_[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR]&&this.getReadyState()==goog.net.XmlHttp.ReadyState.COMPLETE&&2==this.getStatus())goog.log.fine(this.logger_,this.formatMsg_(\"Local request error detected and ignored\"));else if(this.inSend_&&this.getReadyState()==goog.net.XmlHttp.ReadyState.COMPLETE)goog.Timer.callOnce(this.onReadyStateChange_,0,this);else if(this.dispatchEvent(goog.net.EventType.READY_STATE_CHANGE),\nthis.isComplete()){goog.log.fine(this.logger_,this.formatMsg_(\"Request complete\"));this.active_=!1;try{this.isSuccess()?(this.dispatchEvent(goog.net.EventType.COMPLETE),this.dispatchEvent(goog.net.EventType.SUCCESS)):(this.lastErrorCode_=goog.net.ErrorCode.HTTP_ERROR,this.lastError_=this.getStatusText()+\" [\"+this.getStatus()+\"]\",this.dispatchErrors_())}finally{this.cleanUpXhr_()}}};\ngoog.net.XhrIo.prototype.cleanUpXhr_=function(a){if(this.xhr_){this.cleanUpTimeoutTimer_();var b=this.xhr_,c=this.xhrOptions_[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION]?goog.nullFunction:null;this.xhrOptions_=this.xhr_=null;a||this.dispatchEvent(goog.net.EventType.READY);try{b.onreadystatechange=c}catch(d){goog.log.error(this.logger_,\"Problem encountered resetting onreadystatechange: \"+d.message)}}};\ngoog.net.XhrIo.prototype.cleanUpTimeoutTimer_=function(){this.xhr_&&this.useXhr2Timeout_&&(this.xhr_[goog.net.XhrIo.XHR2_ON_TIMEOUT_]=null);goog.isNumber(this.timeoutId_)&&(goog.Timer.clear(this.timeoutId_),this.timeoutId_=null)};goog.net.XhrIo.prototype.isActive=function(){return!!this.xhr_};goog.net.XhrIo.prototype.isComplete=function(){return this.getReadyState()==goog.net.XmlHttp.ReadyState.COMPLETE};\ngoog.net.XhrIo.prototype.isSuccess=function(){var a=this.getStatus();return goog.net.HttpStatus.isSuccess(a)||0===a&&!this.isLastUriEffectiveSchemeHttp_()};goog.net.XhrIo.prototype.isLastUriEffectiveSchemeHttp_=function(){var a=goog.uri.utils.getEffectiveScheme(String(this.lastUri_));return goog.net.XhrIo.HTTP_SCHEME_PATTERN.test(a)};goog.net.XhrIo.prototype.getReadyState=function(){return this.xhr_?this.xhr_.readyState:goog.net.XmlHttp.ReadyState.UNINITIALIZED};\ngoog.net.XhrIo.prototype.getStatus=function(){try{return this.getReadyState()>goog.net.XmlHttp.ReadyState.LOADED?this.xhr_.status:-1}catch(a){return goog.log.warning(this.logger_,\"Can not get status: \"+a.message),-1}};goog.net.XhrIo.prototype.getStatusText=function(){try{return this.getReadyState()>goog.net.XmlHttp.ReadyState.LOADED?this.xhr_.statusText:\"\"}catch(a){return goog.log.fine(this.logger_,\"Can not get status: \"+a.message),\"\"}};goog.net.XhrIo.prototype.getLastUri=function(){return String(this.lastUri_)};\ngoog.net.XhrIo.prototype.getResponseText=function(){try{return this.xhr_?this.xhr_.responseText:\"\"}catch(a){return goog.log.fine(this.logger_,\"Can not get responseText: \"+a.message),\"\"}};goog.net.XhrIo.prototype.getResponseBody=function(){try{if(this.xhr_&&\"responseBody\"in this.xhr_)return this.xhr_.responseBody}catch(a){goog.log.fine(this.logger_,\"Can not get responseBody: \"+a.message)}return null};\ngoog.net.XhrIo.prototype.getResponseXml=function(){try{return this.xhr_?this.xhr_.responseXML:null}catch(a){return goog.log.fine(this.logger_,\"Can not get responseXML: \"+a.message),null}};goog.net.XhrIo.prototype.getResponseJson=function(a){if(this.xhr_){var b=this.xhr_.responseText;a&&0==b.indexOf(a)&&(b=b.substring(a.length));return goog.json.parse(b)}};\ngoog.net.XhrIo.prototype.getResponse=function(){try{if(!this.xhr_)return null;if(\"response\"in this.xhr_)return this.xhr_.response;switch(this.responseType_){case goog.net.XhrIo.ResponseType.DEFAULT:case goog.net.XhrIo.ResponseType.TEXT:return this.xhr_.responseText;case goog.net.XhrIo.ResponseType.ARRAY_BUFFER:if(\"mozResponseArrayBuffer\"in this.xhr_)return this.xhr_.mozResponseArrayBuffer}goog.log.error(this.logger_,\"Response type \"+this.responseType_+\" is not supported on this browser\");return null}catch(a){return goog.log.fine(this.logger_,\n\"Can not get response: \"+a.message),null}};goog.net.XhrIo.prototype.getResponseHeader=function(a){return this.xhr_&&this.isComplete()?this.xhr_.getResponseHeader(a):void 0};goog.net.XhrIo.prototype.getAllResponseHeaders=function(){return this.xhr_&&this.isComplete()?this.xhr_.getAllResponseHeaders():\"\"};\ngoog.net.XhrIo.prototype.getResponseHeaders=function(){for(var a={},b=this.getAllResponseHeaders().split(\"\\r\\n\"),c=0;c<b.length;c++)if(!goog.string.isEmpty(b[c])){var d=goog.string.splitLimit(b[c],\": \",2);a[d[0]]=a[d[0]]?a[d[0]]+(\", \"+d[1]):d[1]}return a};goog.net.XhrIo.prototype.getLastErrorCode=function(){return this.lastErrorCode_};goog.net.XhrIo.prototype.getLastError=function(){return goog.isString(this.lastError_)?this.lastError_:String(this.lastError_)};\ngoog.net.XhrIo.prototype.formatMsg_=function(a){return a+\" [\"+this.lastMethod_+\" \"+this.lastUri_+\" \"+this.getStatus()+\"]\"};goog.debug.entryPointRegistry.register(function(a){goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_=a(goog.net.XhrIo.prototype.onReadyStateChangeEntryPoint_)});goog.Uri=function(a,b){var c;a instanceof goog.Uri?(this.ignoreCase_=goog.isDef(b)?b:a.getIgnoreCase(),this.setScheme(a.getScheme()),this.setUserInfo(a.getUserInfo()),this.setDomain(a.getDomain()),this.setPort(a.getPort()),this.setPath(a.getPath()),this.setQueryData(a.getQueryData().clone()),this.setFragment(a.getFragment())):a&&(c=goog.uri.utils.split(String(a)))?(this.ignoreCase_=!!b,this.setScheme(c[goog.uri.utils.ComponentIndex.SCHEME]||\"\",!0),this.setUserInfo(c[goog.uri.utils.ComponentIndex.USER_INFO]||\n\"\",!0),this.setDomain(c[goog.uri.utils.ComponentIndex.DOMAIN]||\"\",!0),this.setPort(c[goog.uri.utils.ComponentIndex.PORT]),this.setPath(c[goog.uri.utils.ComponentIndex.PATH]||\"\",!0),this.setQueryData(c[goog.uri.utils.ComponentIndex.QUERY_DATA]||\"\",!0),this.setFragment(c[goog.uri.utils.ComponentIndex.FRAGMENT]||\"\",!0)):(this.ignoreCase_=!!b,this.queryData_=new goog.Uri.QueryData(null,null,this.ignoreCase_))};goog.Uri.preserveParameterTypesCompatibilityFlag=!1;goog.Uri.RANDOM_PARAM=goog.uri.utils.StandardQueryParam.RANDOM;\ngoog.Uri.prototype.scheme_=\"\";goog.Uri.prototype.userInfo_=\"\";goog.Uri.prototype.domain_=\"\";goog.Uri.prototype.port_=null;goog.Uri.prototype.path_=\"\";goog.Uri.prototype.fragment_=\"\";goog.Uri.prototype.isReadOnly_=!1;goog.Uri.prototype.ignoreCase_=!1;\ngoog.Uri.prototype.toString=function(){var a=[],b=this.getScheme();b&&a.push(goog.Uri.encodeSpecialChars_(b,goog.Uri.reDisallowedInSchemeOrUserInfo_),\":\");if(b=this.getDomain()){a.push(\"//\");var c=this.getUserInfo();c&&a.push(goog.Uri.encodeSpecialChars_(c,goog.Uri.reDisallowedInSchemeOrUserInfo_),\"@\");a.push(goog.string.urlEncode(b));b=this.getPort();null!=b&&a.push(\":\",String(b))}if(b=this.getPath())this.hasDomain()&&\"/\"!=b.charAt(0)&&a.push(\"/\"),a.push(goog.Uri.encodeSpecialChars_(b,\"/\"==b.charAt(0)?\ngoog.Uri.reDisallowedInAbsolutePath_:goog.Uri.reDisallowedInRelativePath_));(b=this.getEncodedQuery())&&a.push(\"?\",b);(b=this.getFragment())&&a.push(\"#\",goog.Uri.encodeSpecialChars_(b,goog.Uri.reDisallowedInFragment_));return a.join(\"\")};\ngoog.Uri.prototype.resolve=function(a){var b=this.clone(),c=a.hasScheme();c?b.setScheme(a.getScheme()):c=a.hasUserInfo();c?b.setUserInfo(a.getUserInfo()):c=a.hasDomain();c?b.setDomain(a.getDomain()):c=a.hasPort();var d=a.getPath();if(c)b.setPort(a.getPort());else if(c=a.hasPath()){if(\"/\"!=d.charAt(0))if(this.hasDomain()&&!this.hasPath())d=\"/\"+d;else{var e=b.getPath().lastIndexOf(\"/\");-1!=e&&(d=b.getPath().substr(0,e+1)+d)}d=goog.Uri.removeDotSegments(d)}c?b.setPath(d):c=a.hasQuery();c?b.setQueryData(a.getDecodedQuery()):\nc=a.hasFragment();c&&b.setFragment(a.getFragment());return b};goog.Uri.prototype.clone=function(){return new goog.Uri(this)};goog.Uri.prototype.getScheme=function(){return this.scheme_};goog.Uri.prototype.setScheme=function(a,b){this.enforceReadOnly();if(this.scheme_=b?goog.Uri.decodeOrEmpty_(a):a)this.scheme_=this.scheme_.replace(/:$/,\"\");return this};goog.Uri.prototype.hasScheme=function(){return!!this.scheme_};goog.Uri.prototype.getUserInfo=function(){return this.userInfo_};\ngoog.Uri.prototype.setUserInfo=function(a,b){this.enforceReadOnly();this.userInfo_=b?goog.Uri.decodeOrEmpty_(a):a;return this};goog.Uri.prototype.hasUserInfo=function(){return!!this.userInfo_};goog.Uri.prototype.getDomain=function(){return this.domain_};goog.Uri.prototype.setDomain=function(a,b){this.enforceReadOnly();this.domain_=b?goog.Uri.decodeOrEmpty_(a):a;return this};goog.Uri.prototype.hasDomain=function(){return!!this.domain_};goog.Uri.prototype.getPort=function(){return this.port_};\ngoog.Uri.prototype.setPort=function(a){this.enforceReadOnly();if(a){a=Number(a);if(isNaN(a)||0>a)throw Error(\"Bad port number \"+a);this.port_=a}else this.port_=null;return this};goog.Uri.prototype.hasPort=function(){return null!=this.port_};goog.Uri.prototype.getPath=function(){return this.path_};goog.Uri.prototype.setPath=function(a,b){this.enforceReadOnly();this.path_=b?goog.Uri.decodeOrEmpty_(a):a;return this};goog.Uri.prototype.hasPath=function(){return!!this.path_};\ngoog.Uri.prototype.hasQuery=function(){return\"\"!==this.queryData_.toString()};goog.Uri.prototype.setQueryData=function(a,b){this.enforceReadOnly();a instanceof goog.Uri.QueryData?(this.queryData_=a,this.queryData_.setIgnoreCase(this.ignoreCase_)):(b||(a=goog.Uri.encodeSpecialChars_(a,goog.Uri.reDisallowedInQuery_)),this.queryData_=new goog.Uri.QueryData(a,null,this.ignoreCase_));return this};goog.Uri.prototype.setQuery=function(a,b){return this.setQueryData(a,b)};\ngoog.Uri.prototype.getEncodedQuery=function(){return this.queryData_.toString()};goog.Uri.prototype.getDecodedQuery=function(){return this.queryData_.toDecodedString()};goog.Uri.prototype.getQueryData=function(){return this.queryData_};goog.Uri.prototype.getQuery=function(){return this.getEncodedQuery()};goog.Uri.prototype.setParameterValue=function(a,b){this.enforceReadOnly();this.queryData_.set(a,b);return this};\ngoog.Uri.prototype.setParameterValues=function(a,b){this.enforceReadOnly();goog.isArray(b)||(b=[String(b)]);this.queryData_.setValues(a,b);return this};goog.Uri.prototype.getParameterValues=function(a){return this.queryData_.getValues(a)};goog.Uri.prototype.getParameterValue=function(a){return this.queryData_.get(a)};goog.Uri.prototype.getFragment=function(){return this.fragment_};goog.Uri.prototype.setFragment=function(a,b){this.enforceReadOnly();this.fragment_=b?goog.Uri.decodeOrEmpty_(a):a;return this};\ngoog.Uri.prototype.hasFragment=function(){return!!this.fragment_};goog.Uri.prototype.hasSameDomainAs=function(a){return(!this.hasDomain()&&!a.hasDomain()||this.getDomain()==a.getDomain())&&(!this.hasPort()&&!a.hasPort()||this.getPort()==a.getPort())};goog.Uri.prototype.makeUnique=function(){this.enforceReadOnly();this.setParameterValue(goog.Uri.RANDOM_PARAM,goog.string.getRandomString());return this};goog.Uri.prototype.removeParameter=function(a){this.enforceReadOnly();this.queryData_.remove(a);return this};\ngoog.Uri.prototype.setReadOnly=function(a){this.isReadOnly_=a;return this};goog.Uri.prototype.isReadOnly=function(){return this.isReadOnly_};goog.Uri.prototype.enforceReadOnly=function(){if(this.isReadOnly_)throw Error(\"Tried to modify a read-only Uri\");};goog.Uri.prototype.setIgnoreCase=function(a){this.ignoreCase_=a;this.queryData_&&this.queryData_.setIgnoreCase(a);return this};goog.Uri.prototype.getIgnoreCase=function(){return this.ignoreCase_};\ngoog.Uri.parse=function(a,b){return a instanceof goog.Uri?a.clone():new goog.Uri(a,b)};goog.Uri.create=function(a,b,c,d,e,f,g,h){h=new goog.Uri(null,h);a&&h.setScheme(a);b&&h.setUserInfo(b);c&&h.setDomain(c);d&&h.setPort(d);e&&h.setPath(e);f&&h.setQueryData(f);g&&h.setFragment(g);return h};goog.Uri.resolve=function(a,b){a instanceof goog.Uri||(a=goog.Uri.parse(a));b instanceof goog.Uri||(b=goog.Uri.parse(b));return a.resolve(b)};\ngoog.Uri.removeDotSegments=function(a){if(\"..\"==a||\".\"==a)return\"\";if(goog.string.contains(a,\"./\")||goog.string.contains(a,\"/.\")){var b=goog.string.startsWith(a,\"/\");a=a.split(\"/\");for(var c=[],d=0;d<a.length;){var e=a[d++];\".\"==e?b&&d==a.length&&c.push(\"\"):\"..\"==e?((1<c.length||1==c.length&&\"\"!=c[0])&&c.pop(),b&&d==a.length&&c.push(\"\")):(c.push(e),b=!0)}return c.join(\"/\")}return a};goog.Uri.decodeOrEmpty_=function(a){return a?decodeURIComponent(a):\"\"};\ngoog.Uri.encodeSpecialChars_=function(a,b){return goog.isString(a)?encodeURI(a).replace(b,goog.Uri.encodeChar_):null};goog.Uri.encodeChar_=function(a){a=a.charCodeAt(0);return\"%\"+(a>>4&15).toString(16)+(a&15).toString(16)};goog.Uri.reDisallowedInSchemeOrUserInfo_=/[#\\/\\?@]/g;goog.Uri.reDisallowedInRelativePath_=/[\\#\\?:]/g;goog.Uri.reDisallowedInAbsolutePath_=/[\\#\\?]/g;goog.Uri.reDisallowedInQuery_=/[\\#\\?@]/g;goog.Uri.reDisallowedInFragment_=/#/g;\ngoog.Uri.haveSameDomain=function(a,b){var c=goog.uri.utils.split(a),d=goog.uri.utils.split(b);return c[goog.uri.utils.ComponentIndex.DOMAIN]==d[goog.uri.utils.ComponentIndex.DOMAIN]&&c[goog.uri.utils.ComponentIndex.PORT]==d[goog.uri.utils.ComponentIndex.PORT]};goog.Uri.QueryData=function(a,b,c){this.encodedQuery_=a||null;this.ignoreCase_=!!c};\ngoog.Uri.QueryData.prototype.ensureKeyMapInitialized_=function(){if(!this.keyMap_&&(this.keyMap_=new goog.structs.Map,this.count_=0,this.encodedQuery_))for(var a=this.encodedQuery_.split(\"&\"),b=0;b<a.length;b++){var c=a[b].indexOf(\"=\"),d=null,e=null;0<=c?(d=a[b].substring(0,c),e=a[b].substring(c+1)):d=a[b];d=goog.string.urlDecode(d);d=this.getKeyName_(d);this.add(d,e?goog.string.urlDecode(e):\"\")}};\ngoog.Uri.QueryData.createFromMap=function(a,b,c){b=goog.structs.getKeys(a);if(\"undefined\"==typeof b)throw Error(\"Keys are undefined\");c=new goog.Uri.QueryData(null,null,c);a=goog.structs.getValues(a);for(var d=0;d<b.length;d++){var e=b[d],f=a[d];goog.isArray(f)?c.setValues(e,f):c.add(e,f)}return c};\ngoog.Uri.QueryData.createFromKeysValues=function(a,b,c,d){if(a.length!=b.length)throw Error(\"Mismatched lengths for keys/values\");c=new goog.Uri.QueryData(null,null,d);for(d=0;d<a.length;d++)c.add(a[d],b[d]);return c};goog.Uri.QueryData.prototype.keyMap_=null;goog.Uri.QueryData.prototype.count_=null;goog.Uri.QueryData.prototype.getCount=function(){this.ensureKeyMapInitialized_();return this.count_};\ngoog.Uri.QueryData.prototype.add=function(a,b){this.ensureKeyMapInitialized_();this.invalidateCache_();a=this.getKeyName_(a);var c=this.keyMap_.get(a);c||this.keyMap_.set(a,c=[]);c.push(b);this.count_++;return this};goog.Uri.QueryData.prototype.remove=function(a){this.ensureKeyMapInitialized_();a=this.getKeyName_(a);return this.keyMap_.containsKey(a)?(this.invalidateCache_(),this.count_-=this.keyMap_.get(a).length,this.keyMap_.remove(a)):!1};\ngoog.Uri.QueryData.prototype.clear=function(){this.invalidateCache_();this.keyMap_=null;this.count_=0};goog.Uri.QueryData.prototype.isEmpty=function(){this.ensureKeyMapInitialized_();return 0==this.count_};goog.Uri.QueryData.prototype.containsKey=function(a){this.ensureKeyMapInitialized_();a=this.getKeyName_(a);return this.keyMap_.containsKey(a)};goog.Uri.QueryData.prototype.containsValue=function(a){var b=this.getValues();return goog.array.contains(b,a)};\ngoog.Uri.QueryData.prototype.getKeys=function(){this.ensureKeyMapInitialized_();for(var a=this.keyMap_.getValues(),b=this.keyMap_.getKeys(),c=[],d=0;d<b.length;d++)for(var e=a[d],f=0;f<e.length;f++)c.push(b[d]);return c};goog.Uri.QueryData.prototype.getValues=function(a){this.ensureKeyMapInitialized_();var b=[];if(goog.isString(a))this.containsKey(a)&&(b=goog.array.concat(b,this.keyMap_.get(this.getKeyName_(a))));else{a=this.keyMap_.getValues();for(var c=0;c<a.length;c++)b=goog.array.concat(b,a[c])}return b};\ngoog.Uri.QueryData.prototype.set=function(a,b){this.ensureKeyMapInitialized_();this.invalidateCache_();a=this.getKeyName_(a);this.containsKey(a)&&(this.count_-=this.keyMap_.get(a).length);this.keyMap_.set(a,[b]);this.count_++;return this};goog.Uri.QueryData.prototype.get=function(a,b){var c=a?this.getValues(a):[];return goog.Uri.preserveParameterTypesCompatibilityFlag?0<c.length?c[0]:b:0<c.length?String(c[0]):b};\ngoog.Uri.QueryData.prototype.setValues=function(a,b){this.remove(a);0<b.length&&(this.invalidateCache_(),this.keyMap_.set(this.getKeyName_(a),goog.array.clone(b)),this.count_+=b.length)};\ngoog.Uri.QueryData.prototype.toString=function(){if(this.encodedQuery_)return this.encodedQuery_;if(!this.keyMap_)return\"\";for(var a=[],b=this.keyMap_.getKeys(),c=0;c<b.length;c++)for(var d=b[c],e=goog.string.urlEncode(d),d=this.getValues(d),f=0;f<d.length;f++){var g=e;\"\"!==d[f]&&(g+=\"=\"+goog.string.urlEncode(d[f]));a.push(g)}return this.encodedQuery_=a.join(\"&\")};goog.Uri.QueryData.prototype.toDecodedString=function(){return goog.Uri.decodeOrEmpty_(this.toString())};\ngoog.Uri.QueryData.prototype.invalidateCache_=function(){this.encodedQuery_=null};goog.Uri.QueryData.prototype.filterKeys=function(a){this.ensureKeyMapInitialized_();goog.structs.forEach(this.keyMap_,function(b,c,d){goog.array.contains(a,c)||this.remove(c)},this);return this};goog.Uri.QueryData.prototype.clone=function(){var a=new goog.Uri.QueryData;a.encodedQuery_=this.encodedQuery_;this.keyMap_&&(a.keyMap_=this.keyMap_.clone(),a.count_=this.count_);return a};\ngoog.Uri.QueryData.prototype.getKeyName_=function(a){a=String(a);this.ignoreCase_&&(a=a.toLowerCase());return a};goog.Uri.QueryData.prototype.setIgnoreCase=function(a){a&&!this.ignoreCase_&&(this.ensureKeyMapInitialized_(),this.invalidateCache_(),goog.structs.forEach(this.keyMap_,function(a,c){var d=c.toLowerCase();c!=d&&(this.remove(c),this.setValues(d,a))},this));this.ignoreCase_=a};\ngoog.Uri.QueryData.prototype.extend=function(a){for(var b=0;b<arguments.length;b++)goog.structs.forEach(arguments[b],function(a,b){this.add(b,a)},this)};var sae={};appengine={DevSocket:function(a){this.readyState=appengine.DevSocket.ReadyState.CONNECTING;this.applicationKey_=this.token_=a.substring(a.lastIndexOf(\"/\")+1);this.clientId_=null;this.win_=goog.dom.getWindow();this.pollingTimer_=null;goog.net.XhrIo.send(this.getUrl_(\"connect\"),goog.bind(this.connect_,this));goog.events.listen(this.win_,\"beforeunload\",goog.bind(this.beforeunload_,this));if(!document.body)throw\"document.body is not defined -- do not create socket from script in <head>.\";}};\nappengine.DevSocket.POLLING_TIMEOUT_MS=500;appengine.DevSocket.BASE_URL=\"/_sae/channel/\";appengine.DevSocket.ReadyState={CONNECTING:0,OPEN:1,CLOSING:2,CLOSED:3};appengine.DevSocket.prototype.getUrl_=function(a){a=appengine.DevSocket.BASE_URL+\"dev?command=\"+a+\"&channel=\"+this.token_;this.clientId_&&(a+=\"&client=\"+this.clientId_);return a};\nappengine.DevSocket.prototype.connect_=function(a){a=a.target;if(a.isSuccess())this.clientId_=a.getResponseText(),this.readyState=appengine.DevSocket.ReadyState.OPEN,this.onopen(),this.pollingTimer_=this.win_.setTimeout(goog.bind(this.poll_,this),appengine.DevSocket.POLLING_TIMEOUT_MS);else{this.readyState=appengine.DevSocket.ReadyState.CLOSING;var b={};b.description=a.getStatusText();b.code=a.getStatus();this.onerror(b);this.readyState=appengine.DevSocket.ReadyState.CLOSED;this.onclose()}};\nappengine.DevSocket.prototype.disconnect_=function(){this.readyState=appengine.DevSocket.ReadyState.CLOSED;this.onclose()};\nappengine.DevSocket.prototype.forwardMessage_=function(a){a=a.target;if(a.isSuccess()){var b={};b.data=a.getResponseText();if(b.data.length)this.onmessage(b);this.readyState==appengine.DevSocket.ReadyState.OPEN&&(this.pollingTimer_=this.win_.setTimeout(goog.bind(this.poll_,this),appengine.DevSocket.POLLING_TIMEOUT_MS))}else b={},b.description=a.getStatusText(),b.code=a.getStatus(),this.onerror(b),this.readyState=appengine.DevSocket.ReadyState.CLOSED,this.onclose()};\nappengine.DevSocket.prototype.poll_=function(){goog.net.XhrIo.send(this.getUrl_(\"poll\"),goog.bind(this.forwardMessage_,this))};appengine.DevSocket.prototype.beforeunload_=function(){var a=goog.net.XmlHttp();a.open(\"GET\",this.getUrl_(\"disconnect\"),!1);a.send()};appengine.DevSocket.prototype.forwardSendComplete_=function(a){a=a.target;if(!a.isSuccess()){var b={};b.description=a.getStatusText();b.code=a.getStatus();this.onerror(b)}};appengine.DevSocket.prototype.onopen=function(){};\nappengine.DevSocket.prototype.onmessage=function(){};appengine.DevSocket.prototype.onerror=function(){};appengine.DevSocket.prototype.onclose=function(){};appengine.DevSocket.prototype.send=function(a){if(this.readyState!=appengine.DevSocket.ReadyState.OPEN)return!1;var b=appengine.DevSocket.BASE_URL+\"message\",c=new goog.Uri.QueryData;c.set(\"from\",this.applicationKey_);c.set(\"message\",a);goog.net.XhrIo.send(b,goog.bind(this.forwardSendComplete_,this),\"POST\",c.toString());return!0};\nappengine.DevSocket.prototype.close=function(){this.readyState=appengine.DevSocket.ReadyState.CLOSING;this.pollingTimer_&&this.win_.clearTimeout(this.pollingTimer_);goog.net.XhrIo.send(this.getUrl_(\"disconnect\"),goog.bind(this.disconnect_,this))};goog.exportSymbol(\"sae.Channel.prototype.onopen\",appengine.DevSocket.prototype.onopen);goog.exportSymbol(\"sae.Channel.prototype.onmessage\",appengine.DevSocket.prototype.onmessage);goog.exportSymbol(\"sae.Channel.prototype.onerror\",appengine.DevSocket.prototype.onerror);\ngoog.exportSymbol(\"sae.Channel.prototype.onclose\",appengine.DevSocket.prototype.onclose);goog.exportSymbol(\"sae.Channel\",appengine.DevSocket);goog.exportSymbol(\"sae.Channel.ReadyState\",appengine.DevSocket.ReadyState);goog.exportSymbol(\"sae.Channel.prototype.send\",appengine.DevSocket.prototype.send);goog.exportSymbol(\"sae.Channel.prototype.close\",appengine.DevSocket.prototype.close);})()\n"
  },
  {
    "path": "dev_server/sae/channel.py",
    "content": "#!/usr/bin/env python\n# -*-coding: utf8 -*-\n\n\"\"\"Channel API\n\"\"\"\n\nimport time\nimport json\nimport os\n\nMAXIMUM_CLIENT_ID_LENGTH = 118\n\nMAXIMUM_TOKEN_DURATION_SECONDS = 24 * 60\n\nMAXIMUM_MESSAGE_LENGTH = 32767\n\nclass Error(Exception):\n    \"\"\"Base error class for this module.\"\"\"\n\nclass InvalidChannelClientIdError(Error):\n    \"\"\"Error that indicates a bad client id.\"\"\"\n\nclass InvalidChannelTokenDurationError(Error):\n    \"\"\"Error that indicates the requested duration is invalid.\"\"\"\n\nclass InvalidMessageError(Error):\n    \"\"\"Error that indicates a message is malformed.\"\"\"\n\nclass InternalError(Error):\n    \"\"\"Error that indicates server side error\"\"\"\n\ndef _validate_client_id(client_id):\n    if not isinstance(client_id, basestring):\n        raise InvalidChannelClientIdError('\"%s\" is not a string.' % client_id)\n\n    if isinstance(client_id, unicode):\n        client_id = client_id.encode('utf-8')\n\n    if len(client_id) > MAXIMUM_CLIENT_ID_LENGTH:\n        msg = 'Client id length %d is greater than max length %d' % (\n            len(client_id), MAXIMUM_CLIENT_ID_LENGTH)\n        raise InvalidChannelClientIdError(msg)\n\n    return client_id\n\ndef create_channel(name, duration=None):\n    client_id = _validate_client_id(name)\n\n    if duration is not None:\n        if not isinstance(duration, (int, long)):\n            raise InvalidChannelTokenDurationError(\n                'Argument duration must be integral')\n        elif duration < 1:\n            raise InvalidChannelTokenDurationError(\n                'Argument duration must not be less than 1')\n        elif duration > MAXIMUM_TOKEN_DURATION_SECONDS:\n            msg = ('Argument duration must be less than %d'\n                 % (MAXIMUM_TOKEN_DURATION_SECONDS + 1))\n            raise InvalidChannelTokenDurationError(msg)\n\n    _cache[name] = []\n    return 'http://%s/_sae/channel/%s' % (os.environ['HTTP_HOST'], name)\n\ndef send_message(name, message, async=False):\n    client_id = name\n\n    if isinstance(message, unicode):\n        message = message.encode('utf-8')\n    elif not isinstance(message, str):\n        raise InvalidMessageError('Message must be a string')\n    if len(message) > MAXIMUM_MESSAGE_LENGTH:\n        raise InvalidMessageError(\n            'Message must be no longer than %d chars' % MAXIMUM_MESSAGE_LENGTH)\n\n    if name in _cache:\n        _cache[name].append(message)\n        return 1\n    else:\n        return 0\n\n_cache = {}\n\nimport urllib\nimport urlparse\nimport cStringIO\n\ndef _channel_wrapper(app):\n    def _(environ, start_response):\n        if not environ['PATH_INFO'].startswith('/_sae/channel/dev'):\n            return app(environ, start_response)\n\n        qs = urlparse.parse_qs(environ['QUERY_STRING'])\n\n        token = qs['channel'][0]\n        command = qs['command'][0]\n\n        if token not in _cache:\n            start_response('401 forbidden', [])\n            return []\n\n        start_response('200 ok', [])\n\n        if command == 'poll':\n            try:\n                return [_cache[token].pop(0),]\n            except IndexError:\n                return []\n        else:\n            qs = urllib.urlencode({'from': token})\n            environ['PATH_INFO'] = '/_sae/channel/%sed' % command\n            environ['QUERY_STRING'] = ''\n            environ['REQUEST_METHOD'] = 'POST'\n            environ['HTTP_CONTENT_TYPE'] = 'application/x-www-form-urlencoded'\n            environ['HTTP_CONTENT_LENGTH'] = len(qs)\n            environ['wsgi.input'] = cStringIO.StringIO(qs)\n            try:\n                print '[CHANNEL]', [i for i in app(environ, lambda x, y: None)]\n            except Exception:\n                pass\n            return []\n\n    return _ \n"
  },
  {
    "path": "dev_server/sae/channel.src.js",
    "content": "// ==ClosureCompiler==\n// @output_file_name default.js\n// @compilation_level SIMPLE_OPTIMIZATIONS\n// @use_closure_library true\n// @formatting pretty_print\n// ==/ClosureCompiler==\n\ngoog.provide('sae');\n\ngoog.require('goog.dom');\ngoog.require('goog.net.XhrIo');\ngoog.require('goog.Uri.QueryData');\n\nappengine = {};\n\nappengine.DevSocket = function(url) {\n  this.readyState = appengine.DevSocket.ReadyState.CONNECTING;\n  this.token_ = url.substring(url.lastIndexOf(\"/\") + 1);\n  this.applicationKey_ = this.token_;\n  this.clientId_ = null;\n  this.win_ = goog.dom.getWindow();\n  this.pollingTimer_ = null;\n  goog.net.XhrIo.send(this.getUrl_(\"connect\"), goog.bind(this.connect_, this));\n  goog.events.listen(this.win_, \"beforeunload\", goog.bind(this.beforeunload_, this));\n  if(!document.body) {\n    throw\"document.body is not defined -- do not create socket from script in <head>.\";\n  }\n};\n\nappengine.DevSocket.POLLING_TIMEOUT_MS = 500;\nappengine.DevSocket.BASE_URL = \"/_sae/channel/\";\nappengine.DevSocket.ReadyState = {CONNECTING:0, OPEN:1, CLOSING:2, CLOSED:3};\n\nappengine.DevSocket.prototype.getUrl_ = function(command) {\n  var url = appengine.DevSocket.BASE_URL + \"dev?command=\" + command + \"&channel=\" + this.token_;\n  this.clientId_ && (url += \"&client=\" + this.clientId_);\n  return url\n};\n\nappengine.DevSocket.prototype.connect_ = function(e) {\n  var xhr = e.target;\n  if(xhr.isSuccess()) {\n    this.clientId_ = xhr.getResponseText(), this.readyState = appengine.DevSocket.ReadyState.OPEN, this.onopen(), this.pollingTimer_ = this.win_.setTimeout(goog.bind(this.poll_, this), appengine.DevSocket.POLLING_TIMEOUT_MS)\n  }else {\n    this.readyState = appengine.DevSocket.ReadyState.CLOSING;\n    var evt = {};\n    evt.description = xhr.getStatusText();\n    evt.code = xhr.getStatus();\n    this.onerror(evt);\n    this.readyState = appengine.DevSocket.ReadyState.CLOSED;\n    this.onclose()\n  }\n};\n\nappengine.DevSocket.prototype.disconnect_ = function() {\n  this.readyState = appengine.DevSocket.ReadyState.CLOSED;\n  this.onclose()\n};\n\nappengine.DevSocket.prototype.forwardMessage_ = function(e) {\n  var xhr = e.target;\n  if(xhr.isSuccess()) {\n    var evt = {};\n    evt.data = xhr.getResponseText();\n    if(evt.data.length) {\n      this.onmessage(evt)\n    }\n    this.readyState == appengine.DevSocket.ReadyState.OPEN && (this.pollingTimer_ = this.win_.setTimeout(goog.bind(this.poll_, this), appengine.DevSocket.POLLING_TIMEOUT_MS))\n  }else {\n    evt = {}, evt.description = xhr.getStatusText(), evt.code = xhr.getStatus(), this.onerror(evt), this.readyState = appengine.DevSocket.ReadyState.CLOSED, this.onclose()\n  }\n};\n\nappengine.DevSocket.prototype.poll_ = function() {\n  goog.net.XhrIo.send(this.getUrl_(\"poll\"), goog.bind(this.forwardMessage_, this))\n};\n\nappengine.DevSocket.prototype.beforeunload_ = function() {\n  var xhr = goog.net.XmlHttp();\n  xhr.open(\"GET\", this.getUrl_(\"disconnect\"), !1);\n  xhr.send()\n};\n\nappengine.DevSocket.prototype.forwardSendComplete_ = function(e) {\n  var xhr = e.target;\n  if(!xhr.isSuccess()) {\n    var evt = {};\n    evt.description = xhr.getStatusText();\n    evt.code = xhr.getStatus();\n    this.onerror(evt)\n  }\n};\n\nappengine.DevSocket.prototype.onopen = function() {};\nappengine.DevSocket.prototype.onmessage = function() {};\nappengine.DevSocket.prototype.onerror = function() {};\nappengine.DevSocket.prototype.onclose = function() {};\n\nappengine.DevSocket.prototype.send = function(data) {\n  if(this.readyState != appengine.DevSocket.ReadyState.OPEN) {\n    return!1\n  }\n  var url = appengine.DevSocket.BASE_URL + \"message\", sendData = new goog.Uri.QueryData;\n  sendData.set(\"from\", this.applicationKey_);\n  sendData.set(\"message\", data);\n  goog.net.XhrIo.send(url, goog.bind(this.forwardSendComplete_, this), \"POST\", sendData.toString());\n  return!0\n};\n\nappengine.DevSocket.prototype.close = function() {\n  this.readyState = appengine.DevSocket.ReadyState.CLOSING;\n  this.pollingTimer_ && this.win_.clearTimeout(this.pollingTimer_);\n  goog.net.XhrIo.send(this.getUrl_(\"disconnect\"), goog.bind(this.disconnect_, this))\n};\n\ngoog.exportSymbol(\"sae.Channel.prototype.onopen\", appengine.DevSocket.prototype.onopen);\ngoog.exportSymbol(\"sae.Channel.prototype.onmessage\", appengine.DevSocket.prototype.onmessage);\ngoog.exportSymbol(\"sae.Channel.prototype.onerror\", appengine.DevSocket.prototype.onerror);\ngoog.exportSymbol(\"sae.Channel.prototype.onclose\", appengine.DevSocket.prototype.onclose);\ngoog.exportSymbol(\"sae.Channel\", appengine.DevSocket);\ngoog.exportSymbol(\"sae.Channel.ReadyState\", appengine.DevSocket.ReadyState);\ngoog.exportSymbol(\"sae.Channel.prototype.send\", appengine.DevSocket.prototype.send);\ngoog.exportSymbol(\"sae.Channel.prototype.close\", appengine.DevSocket.prototype.close);\n"
  },
  {
    "path": "dev_server/sae/conf.py",
    "content": "\"\"\" SAE Settings\"\"\"\n\nimport os\n\nSAE_STOREHOST = 'http://stor.sae.sina.com.cn/storageApi.php'\nSAE_S3HOST = 'http://s3.sae.sina.com.cn/s3Api.php'\nSAE_TMP_PATH = '$SAE_TMPFS_PATH'\n\nSAE_MYSQL_HOST_M = 'w.rdc.sae.sina.com.cn'\nSAE_MYSQL_HOST_S = 'r.rdc.sae.sina.com.cn'\nSAE_MYSQL_PORT = '3307'\n\nSAE_FETCHURL_HOST = 'http://fetchurl.sae.sina.com.cn'\n\n"
  },
  {
    "path": "dev_server/sae/const.py",
    "content": "\"\"\"Constants about app\n\n\"\"\"\nimport os\nimport conf\n\n# Private\nAPP_NAME = os.environ.get('APP_NAME', '')\nAPP_HASH = os.environ.get('APP_HASH', '')\nACCESS_KEY = os.environ.get('ACCESS_KEY', '')\nSECRET_KEY = os.environ.get('SECRET_KEY', '')\n\n# Public\nMYSQL_DB = '_'.join(['app', APP_NAME])\nMYSQL_USER = ACCESS_KEY\nMYSQL_PASS = SECRET_KEY\nMYSQL_HOST = conf.SAE_MYSQL_HOST_M\nMYSQL_PORT = conf.SAE_MYSQL_PORT\nMYSQL_HOST_S = conf.SAE_MYSQL_HOST_S\n"
  },
  {
    "path": "dev_server/sae/core.py",
    "content": "\"\"\"Core functions of SAE\n\nenviron    A copy of the environ passed to your wsgi app, should not be used directly\n\n\"\"\"\n\ndef get_access_key():\n    \"\"\"Return access_key of your app\"\"\"\n    return environ.get('HTTP_ACCESSKEY', '')\n\ndef get_secret_key():\n    \"\"\"Return secret_key of your app\"\"\"\n    return environ.get('HTTP_SECRETKEY', '')\n\ndef get_trusted_hosts():\n    return [host for host in environ.get('TRUSTED_HOSTS', '').split() if host]\n\nenviron = {}\n"
  },
  {
    "path": "dev_server/sae/ext/__init__.py",
    "content": ""
  },
  {
    "path": "dev_server/sae/ext/django/__init__.py",
    "content": ""
  },
  {
    "path": "dev_server/sae/ext/django/mail/__init__.py",
    "content": ""
  },
  {
    "path": "dev_server/sae/ext/django/mail/backend.py",
    "content": "\"\"\"send mail via sae's mail service\"\"\"\n\nimport threading\n\nfrom django.conf import settings\nfrom django.core.mail.backends.base import BaseEmailBackend\n\nfrom email.mime.base import MIMEBase\n\nfrom sae.mail import EmailMessage, Error\n\nclass EmailBackend(BaseEmailBackend):\n    def __init__(self, host=None, port=None, username=None, password=None,\n                 use_tls=None, fail_silently=False, **kwargs):\n        super(EmailBackend, self).__init__(fail_silently=fail_silently)\n        self.host = host or settings.EMAIL_HOST\n        self.port = port or settings.EMAIL_PORT\n        if username is None:\n            self.username = settings.EMAIL_HOST_USER\n        else:\n            self.username = username\n        if password is None:\n            self.password = settings.EMAIL_HOST_PASSWORD\n        else:\n            self.password = password\n        if use_tls is None:\n            self.use_tls = settings.EMAIL_USE_TLS\n        else:\n            self.use_tls = use_tls\n        self.smtp = (self.host, self.port, self.username, self.password,\n                     self.use_tls)\n        self._lock = threading.RLock()\n\n    def send_messages(self, email_messages):\n        if not email_messages:\n            return\n        with self._lock:\n            num_sent = 0\n            for message in email_messages:\n                sent = self._send(message)\n                if sent:\n                    num_sent += 1\n        return num_sent\n\n    def _send(self, email_message):\n        if not email_message.recipients():\n            return False\n        attachments = []\n        for attach in email_message.attachments:\n            if isinstance(attach, MIMEBase):\n                if not self.fail_silently:\n                    raise NotImplemented()\n                else:\n                    return False\n            else:\n                attachments.append((attach[0], attach[1]))\n        try:\n            message = EmailMessage()\n            message.to = email_message.recipients()\n            message.from_addr = email_message.from_email\n            message.subject = email_message.subject\n            message.body = email_message.body\n            message.smtp = self.smtp\n            if attachments:\n                message.attachments = attachments\n            message.send()\n        except Error, e:\n            if not self.fail_silently:\n                raise\n            return False\n        return True\n"
  },
  {
    "path": "dev_server/sae/ext/django/storage/__init__.py",
    "content": ""
  },
  {
    "path": "dev_server/sae/ext/django/storage/backend.py",
    "content": "import sys\nfrom StringIO import StringIO\n\nfrom django.conf import settings\nfrom django.core.files.base import File\nfrom django.core.files.storage import Storage\nfrom django.core.exceptions import ImproperlyConfigured\n\nfrom sae.storage import Connection, Error\n\nfrom sae.const import ACCESS_KEY, SECRET_KEY, APP_NAME\n\nSTORAGE_BUCKET_NAME = getattr(settings, 'STORAGE_BUCKET_NAME')\nSTORAGE_ACCOUNT = getattr(settings, 'STORAGE_ACCOUNT', APP_NAME)\nSTORAGE_ACCESSKEY = getattr(settings, 'STORAGE_ACCESSKEY', ACCESS_KEY)\nSTORAGE_SECRETKEY = getattr(settings, 'STORAGE_SECRETKEY', SECRET_KEY)\nSTORAGE_GZIP = getattr(settings, 'STORAGE_GZIP', False)\n\nclass Storage(Storage):\n    def __init__(self, bucket_name=STORAGE_BUCKET_NAME,\n                 accesskey=STORAGE_ACCESSKEY, secretkey=STORAGE_SECRETKEY,\n                 account=STORAGE_ACCOUNT):\n        conn = Connection(accesskey, secretkey, account)\n        self.bucket = conn.get_bucket(bucket_name)\n\n    def _open(self, name, mode='rb'):\n        name = self._normalize_name(name)\n        return StorageFile(name, mode, self)\n\n    def _save(self, name, content):\n        name = self._normalize_name(name)\n        try:\n            self.bucket.put_object(name, content)\n        except Error, e:\n            raise IOError('Storage Error: %s' % e.args)\n        return name\n\n    def delete(self, name):\n        name = self._normalize_name(name)\n        try:\n            self.bucket.delete_object(name)\n        except Error, e:\n            raise IOError('Storage Error: %s' % e.args)\n\n    def exists(self, name):\n        name = self._normalize_name(name)\n        try:\n            self.bucket.stat_object(name)\n        except Error, e:\n            if e[0] == 404:\n                return False\n            raise\n        return True\n\n    #def listdir(self, path):\n    #    path = self._normalize_name(path)\n    #    try:\n    #        result = self.bucket.list(path=path)\n    #        return [i.name for i in result]\n    #    except Error, e:\n    #        raise IOError('Storage Error: %s' % e.args)\n\n    def size(self, name):\n        name = self._normalize_name(name)\n        try:\n            attrs = self.bucket.stat_object(name)\n            return attrs.bytes\n        except Error, e:\n            raise IOError('Storage Error: %s' % e.args)\n\n    def url(self, name):\n        name = self._normalize_name(name)\n        return self.bucket.generate_url(name)\n\n    def _open_read(self, name):\n        name = self._normalize_name(name)\n        class _:\n            def __init__(self, chunks):\n                self.buf = ''\n            def read(self, num_bytes=None):\n                if num_bytes is None:\n                    num_bytes = sys.maxint\n                try:\n                    while len(self.buf) < num_bytes:\n                        self.buf += chunks.next()\n                except StopIteration:\n                    pass\n                except Error, e:\n                    raise IOError('Storage Error: %s' % e.args)\n                retval = self.buf[:num_bytes]\n                self.buf = self.buf[num_bytes:]\n                return retval\n        chunks = self.bucket.get_object_contents(name, chunk_size=8192)\n        return _(chunks)\n\n    def _normalize_name(self, name):\n        return name.lstrip('/')\n\nclass StorageFile(File):\n    def __init__(self, name, mode, storage):\n        self.name = name\n        self.mode = mode\n        self.file = StringIO()\n        self._storage = storage\n        self._is_dirty = False\n\n    @property\n    def size(self):\n        if hasattr(self, '_size'):\n            self._size = self.storage.size()\n        return self._size\n\n    def read(self, num_bytes=None):\n        if not hasattr(self, '_obj'):\n            self._obj = self._storage._open_read(self.name)\n        return self._obj.read(num_bytes)\n\n    def write(self, content):\n        if 'w' not in self._mode:\n            raise AttributeError(\"File was opened for read-only access.\")\n        self.file = StringIO(content)\n        self._is_dirty = True\n\n    def close(self):\n        if self._is_dirty:\n            self._storage._save(self.name, self.file.getvalue())\n        self.file.close()\n"
  },
  {
    "path": "dev_server/sae/ext/shell.py",
    "content": "\n# Copyright (C) 2012-2013 SINA, All rights reserved.\n\nShellMiddleware = lambda x: x\n"
  },
  {
    "path": "dev_server/sae/ext/storage/__init__.py",
    "content": ""
  },
  {
    "path": "dev_server/sae/ext/storage/monkey.py",
    "content": "\n# Copyright (C) 2012-2013 SINA, All rights reserved.\n\nimport os.path\nimport sys\nimport re\nimport time\nimport errno\n\nfrom sae.storage import Connection, Error\n\n_S_FILEPATH_REGEX = re.compile('^(?:/s|/s/.*)$')\n_S_FILENAME_REGEX = re.compile('^(?:/s|/s/([^/]*)/?(.*))$')\n\ndef _parse_name(filename):\n    m = _S_FILENAME_REGEX.match(os.path.normpath(filename))\n    if m:\n        return m.groups()\n    else:\n        raise ValueError('invalid filename')\n\nSTORAGE_PATH = os.environ.get('sae.storage.path')\n\n_is_storage_path = lambda n: _S_FILEPATH_REGEX.match(n)\ndef _get_storage_path(path):\n    if not STORAGE_PATH:\n        raise RuntimeError(\n            \"Please specify --storage-path in the command line\")\n    return STORAGE_PATH + n[2:]\n\nclass _File(file):\n\n    def isatty(self):\n        return False\n\n    # Unimplemented interfaces below here.\n\n    def flush(self):\n        pass\n\n    def fileno(self):\n        raise NotImplementedError()\n\n    def next(self):\n        raise NotImplementedError()\n\n    def readinto(self):\n        raise NotImplementedError()\n\n    def readline(self):\n        raise NotImplementedError()\n\n    def readlines(self):\n        raise NotImplementedError()\n\n    def truncate(self):\n        raise NotImplementedError()\n\n    def writelines(self):\n        raise NotImplementedError()\n\n    def xreadlines(self):\n        raise NotImplementedError()\n\nimport __builtin__\n\n_real_open = __builtin__.open\ndef open(filename, mode='r', buffering=-1):\n    if _is_storage_path(filename):\n        filename = _get_storage_path(filename)\n    return _real_open(filename, mode, buffering)\n\nimport os\n\n_real_os_listdir = os.listdir\ndef os_listdir(path):\n    if _is_storage_path(path):\n        path = _get_storage_path(path)\n    return _real_os_listdir(path)\n\n_real_os_mkdir = os.mkdir\ndef os_mkdir(path, mode=0777):\n    if _is_storage_path(path):\n        path = _get_storage_path(path)\n    return _real_os_mkdir(path, mode)\n\n_real_os_open = os.open\ndef os_open(filename, flag, mode=0777):\n    if _is_storage_path(filename):\n        filename = _get_storage_path(filename)\n    return  _real_os_open(filename, flag, mode)\n\n_real_os_fdopen = getattr(os, 'fdopen', None)\ndef os_fdopen(fd, mode='r', bufsize=-1):\n    return _real_os_fdopen(fd, mode, bufsize)\n\n_real_os_close = os.close\ndef os_close(fd):\n    return _real_os_close(fd)\n\n_real_os_chmod = os.chmod\ndef os_chmod(path, mode):\n    if _is_storage_path(path):\n        pass\n    else:\n        return _real_os_chmod(path, mode)\n\n_real_os_stat = os.stat\ndef os_stat(path):\n    if _is_storage_path(path):\n        path = _get_storage_path(path)\n    return _real_os_stat(path)\n\n_real_os_unlink = os.unlink\ndef os_unlink(path):\n    if _is_storage_path(path):\n        path = _get_storage_path(path)\n    return _real_os_unlink(path)\n\nimport os.path\n\n_real_os_path_exists = os.path.exists\ndef os_path_exists(path):\n    if _is_storage_path(path):\n        path = _get_storage_path(path)\n    return _real_os_path_exists(path)\n\n_real_os_path_isdir = os.path.isdir\ndef os_path_isdir(path):\n    if _is_storage_path(path):\n        path = _get_storage_path(path)\n    return _real_os_path_isdir(path)\n\n_real_os_rmdir = os.rmdir\ndef os_rmdir(path):\n    if _is_storage_path(path):\n        path = _get_storage_path(path)\n    return _real_os_rmdir(path)\n\ndef patch_all():\n    __builtin__.open = open\n    os.listdir = os_listdir\n    os.mkdir = os_mkdir\n    os.path.exists = os_path_exists\n    os.path.isdir = os_path_isdir\n    os.open = os_open\n    os.fdopen = os_fdopen\n    os.close = os_close\n    os.chmod = os_chmod\n    os.stat = os_stat\n    os.unlink = os_unlink\n    os.rmdir = os_rmdir\n"
  },
  {
    "path": "dev_server/sae/kvdb.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"\nFake client for sae kvdb service.\n\nThis should give you a feel for how this module operates::\n\n    import kvdb\n    kv = kvdb.KVClient()\n\n    kv.set(\"some_key\", \"Some value\")\n    value = kv.get(\"some_key\")\n\n    kv.set(\"another_key\", 3)\n    kv.delete(\"another_key\")\n\"\"\"\n\nimport sys\nimport os\nimport re\nimport time\nimport pickle\n\nSERVER_MAX_KEY_LENGTH = 250\n#  Storing values larger than 1MB requires recompiling memcached.  If you do,\n#  this value can be changed by doing \"memcache.SERVER_MAX_VALUE_LENGTH = N\"\n#  after importing this module.\nSERVER_MAX_VALUE_LENGTH = 1024*1024\n\nclass _Error(Exception):\n    pass\n\nclass _ConnectionDeadError(Exception):\n    pass\n\nclass _CacheEntry(object):\n    \n    def __init__(self, value, flags, expiration):\n        self.value = value\n        self.flags = flags\n        self.created_time = time.time()\n        self.will_expire = expiration != 0\n        self.locked = False\n        self._set_expiration(expiration)\n\n    def _set_expiration(self, expiration):\n        if expiration > (86400 * 30):\n            self.expiration = expiration\n        else:\n            self.expiration = self.created_time + expiration\n\n    def is_expired(self):\n        return self.will_expire and time.time() > self.expiration\n\nclass local(object):\n    pass\n\n_DEAD_RETRY = 30  # number of seconds before retrying a dead server.\n_SOCKET_TIMEOUT = 3  #  number of seconds before sockets timeout.\n\n_cache = {}\n\nclass Client(local):\n    \"\"\"\n    Object representing a pool of memcache servers.\n\n    See L{memcache} for an overview.\n\n    In all cases where a key is used, the key can be either:\n        1. A simple hashable type (string, integer, etc.).\n        2. A tuple of C{(hashvalue, key)}.  This is useful if you want to avoid\n        making this module calculate a hash value.  You may prefer, for\n        example, to keep all of a given user's objects on the same memcache\n        server, so you could use the user's unique id as the hash value.\n\n    @group Setup: __init__, set_servers, forget_dead_hosts, disconnect_all, debuglog\n    @group Insertion: set, add, replace, set_multi\n    @group Retrieval: get, get_multi\n    @group Integers: incr, decr\n    @group Removal: delete, delete_multi\n    @sort: __init__, set_servers, forget_dead_hosts, disconnect_all, debuglog,\\\n           set, set_multi, add, replace, get, get_multi, incr, decr, delete, delete_multi\n    \"\"\"\n    _FLAG_PICKLE  = 1<<0\n    _FLAG_INTEGER = 1<<1\n    _FLAG_LONG    = 1<<2\n    _FLAG_COMPRESSED = 1<<3\n\n    _SERVER_RETRIES = 10  # how many times to try finding a free server.\n\n    # exceptions for Client\n    class MemcachedKeyError(Exception):\n        pass\n    class MemcachedKeyLengthError(MemcachedKeyError):\n        pass\n    class MemcachedKeyCharacterError(MemcachedKeyError):\n        pass\n    class MemcachedKeyNoneError(MemcachedKeyError):\n        pass\n    class MemcachedKeyTypeError(MemcachedKeyError):\n        pass\n    class MemcachedStringEncodingError(Exception):\n        pass\n\n    def __init__(self, servers=[], debug=0, pickleProtocol=0,\n                 pickler=pickle.Pickler, unpickler=pickle.Unpickler,\n                 pload=None, pid=None,\n                 server_max_key_length=SERVER_MAX_KEY_LENGTH,\n                 server_max_value_length=SERVER_MAX_VALUE_LENGTH,\n                 dead_retry=_DEAD_RETRY, socket_timeout=_SOCKET_TIMEOUT,\n                 cache_cas = False):\n        \"\"\"\n        Create a new Client object with the given list of servers.\n\n        @param servers: C{servers} is passed to L{set_servers}.\n        @param debug: whether to display error messages when a server can't be\n        contacted.\n        @param pickleProtocol: number to mandate protocol used by (c)Pickle.\n        @param pickler: optional override of default Pickler to allow subclassing.\n        @param unpickler: optional override of default Unpickler to allow subclassing.\n        @param pload: optional persistent_load function to call on pickle loading.\n        Useful for cPickle since subclassing isn't allowed.\n        @param pid: optional persistent_id function to call on pickle storing.\n        Useful for cPickle since subclassing isn't allowed.\n        @param dead_retry: number of seconds before retrying a blacklisted\n        server. Default to 30 s.\n        @param socket_timeout: timeout in seconds for all calls to a server. Defaults\n        to 3 seconds.\n        @param cache_cas: (default False) If true, cas operations will be\n        cached.  WARNING: This cache is not expired internally, if you have\n        a long-running process you will need to expire it manually via\n        \"client.reset_cas(), or the cache can grow unlimited.\n        @param server_max_key_length: (default SERVER_MAX_KEY_LENGTH)\n        Data that is larger than this will not be sent to the server.\n        @param server_max_value_length: (default SERVER_MAX_VALUE_LENGTH)\n        Data that is larger than this will not be sent to the server.\n        \"\"\"\n        local.__init__(self)\n        self.debug = debug\n        self.cache_cas = cache_cas\n        self.reset_cas()\n\n        # Allow users to modify pickling/unpickling behavior\n        self.server_max_key_length = server_max_key_length\n        self.server_max_value_length = server_max_value_length\n\n        _cache = {}\n\n        self.reset_stats()\n\n    def reset_stats(self):\n        self._get_hits = 0\n        self._get_misses = 0\n        self._cmd_set = 0\n        self._cmd_get = 0\n\n    def reset_cas(self):\n        \"\"\"\n        Reset the cas cache.  This is only used if the Client() object\n        was created with \"cache_cas=True\".  If used, this cache does not\n        expire internally, so it can grow unbounded if you do not clear it\n        yourself.\n        \"\"\"\n        self.cas_ids = {}\n\n    def set_servers(self, servers):\n        \"\"\"\n        Set the pool of servers used by this client.\n\n        @param servers: an array of servers.\n        Servers can be passed in two forms:\n            1. Strings of the form C{\"host:port\"}, which implies a default weight of 1.\n            2. Tuples of the form C{(\"host:port\", weight)}, where C{weight} is\n            an integer weight value.\n        \"\"\"\n        pass\n\n    def get_info(self, stat_args = None):\n        '''Get statistics from each of the servers.\n\n        @param stat_args: Additional arguments to pass to the memcache\n            \"stats\" command.\n\n        @return: A list of tuples ( server_identifier, stats_dictionary ).\n            The dictionary contains a number of name/value pairs specifying\n            the name of the status field and the string value associated with\n            it.  The values are not converted from strings.\n        '''\n\n        info = {\n            'outbytes': 41, \n            'total_size': 22, \n            'inbytes': 62, \n            'set_count': 16, \n            'delete_count': 0, \n            'total_count': 4, \n            'get_count': 11\n        }\n\n        return info\n\n    def debuglog(self, str):\n        if self.debug:\n            sys.stderr.write(\"MemCached: %s\\n\" % str)\n\n    def forget_dead_hosts(self):\n        \"\"\"\n        Reset every host in the pool to an \"alive\" state.\n        \"\"\"\n        pass\n\n    def disconnect_all(self):\n        pass\n\n    def delete(self, key):\n        '''Deletes a key from the memcache.\n\n        @return: Nonzero on success.\n        '''\n        if key not in _cache:\n            return False\n        del _cache[key]\n        return True\n\n    def add(self, key, val, time = 0, min_compress_len = 0):\n        '''\n        Add new key with value.\n\n        Like L{set}, but only stores in memcache if the key doesn't already exist.\n\n        @return: Nonzero on success.\n        @rtype: int\n        '''\n        return self._set(\"add\", key, val, time, min_compress_len)\n\n    def replace(self, key, val, time=0, min_compress_len=0):\n        '''Replace existing key with value.\n\n        Like L{set}, but only stores in memcache if the key already exists.\n        The opposite of L{add}.\n\n        @return: Nonzero on success.\n        @rtype: int\n        '''\n        return self._set(\"replace\", key, val, time, min_compress_len)\n\n    def set(self, key, val, time=0, min_compress_len=0):\n        '''Unconditionally sets a key to a given value in the memcache.\n\n        The C{key} can optionally be an tuple, with the first element\n        being the server hash value and the second being the key.\n        If you want to avoid making this module calculate a hash value.\n        You may prefer, for example, to keep all of a given user's objects\n        on the same memcache server, so you could use the user's unique\n        id as the hash value.\n\n        @return: Nonzero on success.\n        @rtype: int\n        @param time: Tells memcached the time which this value should expire, either\n        as a delta number of seconds, or an absolute unix time-since-the-epoch\n        value. See the memcached protocol docs section \"Storage Commands\"\n        for more info on <exptime>. We default to 0 == cache forever.\n        @param min_compress_len: The threshold length to kick in auto-compression\n        of the value using the zlib.compress() routine. If the value being cached is\n        a string, then the length of the string is measured, else if the value is an\n        object, then the length of the pickle result is measured. If the resulting\n        attempt at compression yeilds a larger string than the input, then it is\n        discarded. For backwards compatability, this parameter defaults to 0,\n        indicating don't ever try to compress.\n        '''\n        return self._set(\"set\", key, val, time, min_compress_len)\n\n    def _set(self, cmd, key, val, time, min_compress_len = 0):\n        self.check_key(key)\n\n        self._cmd_set += 1\n\n        key_exists = key in _cache\n\n        if ((cmd == 'add' and key_exists) or\n            (cmd == 'replace' and not key_exists) or\n            (cmd == 'prepend' and not key_exists) or\n            (cmd == 'append' and not key_exists)):\n            return False\n\n        if cmd == 'prepend':\n            new_val = val + _cache[key].value\n        elif cmd == 'append':\n            new_val = _cache[key].value + val\n        else:\n            new_val = val\n\n        _cache[key] = _CacheEntry(new_val, 0, time)\n        return True\n\n    def _get(self, cmd, key):\n        self.check_key(key)\n\n        self._cmd_get += 1\n\n        if key in _cache:\n            entry = _cache[key]\n            if not entry.is_expired():\n                self._get_hits += 1\n                return entry.value\n        self._get_misses += 1\n        return None\n\n    def get(self, key):\n        '''Retrieves a key from the memcache.\n\n        @return: The value or None.\n        '''\n        return self._get('get', key)\n\n    def get_multi(self, keys, key_prefix=''):\n        '''\n        Retrieves multiple keys from the memcache doing just one query.\n\n        >>> success = mc.set(\"foo\", \"bar\")\n        >>> success = mc.set(\"baz\", 42)\n        >>> mc.get_multi([\"foo\", \"baz\", \"foobar\"]) == {\"foo\": \"bar\", \"baz\": 42}\n        1\n\n        get_mult [ and L{set_multi} ] can take str()-ables like ints / longs as keys too. Such as your db pri key fields.\n        They're rotored through str() before being passed off to memcache, with or without the use of a key_prefix.\n        In this mode, the key_prefix could be a table name, and the key itself a db primary key number.\n\n        This method is recommended over regular L{get} as it lowers the number of\n        total packets flying around your network, reducing total latency, since\n        your app doesn't have to wait for each round-trip of L{get} before sending\n        the next one.\n\n        See also L{set_multi}.\n\n        @param keys: An array of keys.\n        @param key_prefix: A string to prefix each key when we communicate with memcache.\n            Facilitates pseudo-namespaces within memcache. Returned dictionary keys will not have this prefix.\n        @return:  A dictionary of key/value pairs that were available. If key_prefix was provided, the keys in the retured dictionary will not have it present.\n\n        '''\n        retval = {}\n        for e in keys:\n            _key = key_prefix + str(e)\n            val = self._get('get', _key)\n            if val is not None:\n                retval[e] = val\n        return retval\n\n    def get_by_prefix(self, prefix, limit=None, max_count=None,\n                      marker=None, start_key=None):\n        '''\n        >>> success = mc.set('k1', 1)\n        >>> success = mc.set('k2', 2)\n        >>> success = mc.set('xyz', 'xxxxxxx')\n        >>> mc.get_by_prefix('k') == [('k2', 2), ('k1', 1)]\n        1\n\n        '''\n        start_key = marker or start_key\n        max_count = limit or max_count or 100\n\n        ignore = False\n        if start_key is not None:\n            ignore = True\n\n        for k, e in _cache.iteritems():\n            if ignore:\n                if k == start_key:\n                    ignore = False\n                continue\n\n            if e.is_expired():\n                continue\n\n            if max_count <= 0: break\n\n            if str(k).startswith(prefix):\n                max_count -= 1\n                yield k, e.value\n\n    def getkeys_by_prefix(self, prefix, limit=None, max_count=None,\n                          marker=None, start_key=None):\n        max_count = limit or max_count\n        marker = marker or start_key\n        kv = self.get_by_prefix(prefix, max_count, marker=marker)\n        return [e[0] for e in kv]\n\n    def check_key(self, key, key_extra_len=0):\n        \"\"\"Checks sanity of key.  Fails if:\n            Key length is > SERVER_MAX_KEY_LENGTH (Raises MemcachedKeyLength).\n            Contains control characters  (Raises MemcachedKeyCharacterError).\n            Is not a string (Raises MemcachedStringEncodingError)\n            Is an unicode string (Raises MemcachedStringEncodingError)\n            Is not a string (Raises MemcachedKeyError)\n            Is None (Raises MemcachedKeyError)\n        \"\"\"\n        if isinstance(key, tuple): key = key[1]\n        if not key:\n            raise Client.MemcachedKeyNoneError(\"Key is None\")\n        if isinstance(key, unicode):\n            raise Client.MemcachedStringEncodingError(\n                    \"Keys must be str()'s, not unicode.  Convert your unicode \"\n                    \"strings using mystring.encode(charset)!\")\n        if not isinstance(key, str):\n            raise Client.MemcachedKeyTypeError(\"Key must be str()'s\")\n\n        if isinstance(key, basestring):\n            if self.server_max_key_length != 0 and \\\n                len(key) + key_extra_len > self.server_max_key_length:\n                raise Client.MemcachedKeyLengthError(\"Key length is > %s\"\n                         % self.server_max_key_length)\n            for char in key:\n                if ord(char) < 33 or ord(char) == 127:\n                    raise Client.MemcachedKeyCharacterError(\n                            \"Control characters not allowed\")\n\nKVClient = Client\n\ndef _doctest():\n    import doctest, kvdb\n    servers = [\"127.0.0.1:11211\"]\n    mc = Client(servers, debug=1)\n    globs = {\"mc\": mc}\n    return doctest.testmod(kvdb, globs=globs)\n\nif __name__ == \"__main__\":\n    failures = 0\n    print \"Testing docstrings...\"\n    _doctest()\n    print \"Running tests:\"\n    print\n    serverList = [[\"127.0.0.1:11211\"]]\n    if '--do-unix' in sys.argv:\n        serverList.append([os.path.join(os.getcwd(), 'memcached.socket')])\n\n    for servers in serverList:\n        mc = KVClient(servers, debug=1)\n\n        def to_s(val):\n            if not isinstance(val, basestring):\n                return \"%s (%s)\" % (val, type(val))\n            return \"%s\" % val\n        def test_setget(key, val):\n            global failures\n            print \"Testing set/get {'%s': %s} ...\" % (to_s(key), to_s(val)),\n            mc.set(key, val)\n            newval = mc.get(key)\n            if newval == val:\n                print \"OK\"\n                return 1\n            else:\n                print \"FAIL\"; failures = failures + 1\n                return 0\n\n\n        class FooStruct(object):\n            def __init__(self):\n                self.bar = \"baz\"\n            def __str__(self):\n                return \"A FooStruct\"\n            def __eq__(self, other):\n                if isinstance(other, FooStruct):\n                    return self.bar == other.bar\n                return 0\n\n        test_setget(\"a_string\", \"some random string\")\n        test_setget(\"an_integer\", 42)\n        if test_setget(\"long\", long(1<<30)):\n            print \"Testing delete ...\",\n            if mc.delete(\"long\"):\n                print \"OK\"\n            else:\n                print \"FAIL\"; failures = failures + 1\n            print \"Checking results of delete ...\"\n            if mc.get(\"long\") == None:\n                print \"OK\"\n            else:\n                print \"FAIL\"; failures = failures + 1\n        print \"Testing get_multi ...\",\n        print mc.get_multi([\"a_string\", \"an_integer\"])\n\n        #  removed from the protocol\n        #if test_setget(\"timed_delete\", 'foo'):\n        #    print \"Testing timed delete ...\",\n        #    if mc.delete(\"timed_delete\", 1):\n        #        print \"OK\"\n        #    else:\n        #        print \"FAIL\"; failures = failures + 1\n        #    print \"Checking results of timed delete ...\"\n        #    if mc.get(\"timed_delete\") == None:\n        #        print \"OK\"\n        #    else:\n        #        print \"FAIL\"; failures = failures + 1\n\n        print \"Testing get(unknown value) ...\",\n        print to_s(mc.get(\"unknown_value\"))\n\n        f = FooStruct()\n        test_setget(\"foostruct\", f)\n\n        #print \"Testing incr ...\",\n        #x = mc.incr(\"an_integer\", 1)\n        #if x == 43:\n        #    print \"OK\"\n        #else:\n        #    print \"FAIL\"; failures = failures + 1\n\n        #print \"Testing decr ...\",\n        #x = mc.decr(\"an_integer\", 1)\n        #if x == 42:\n        #    print \"OK\"\n        #else:\n        #    print \"FAIL\"; failures = failures + 1\n        sys.stdout.flush()\n\n        # sanity tests\n        print \"Testing sending spaces...\",\n        sys.stdout.flush()\n        try:\n            x = mc.set(\"this has spaces\", 1)\n        except Client.MemcachedKeyCharacterError, msg:\n            print \"OK\"\n        else:\n            print \"FAIL\"; failures = failures + 1\n\n        print \"Testing sending control characters...\",\n        try:\n            x = mc.set(\"this\\x10has\\x11control characters\\x02\", 1)\n        except Client.MemcachedKeyCharacterError, msg:\n            print \"OK\"\n        else:\n            print \"FAIL\"; failures = failures + 1\n\n        print \"Testing using insanely long key...\",\n        try:\n            x = mc.set('a'*SERVER_MAX_KEY_LENGTH, 1)\n        except Client.MemcachedKeyLengthError, msg:\n            print \"FAIL\"; failures = failures + 1\n        else:\n            print \"OK\"\n        try:\n            x = mc.set('a'*SERVER_MAX_KEY_LENGTH + 'a', 1)\n        except Client.MemcachedKeyLengthError, msg:\n            print \"OK\"\n     \ndb_file = os.environ.get('sae.kvdb.file')\nif db_file:\n    import pickle\n    def _save_cache():\n        # XXX: reloader should not do this\n        if not os.environ.get('sae.run_main'): return\n        try:\n            pickle.dump(_cache, open(db_file, 'wb'))\n        except Exception, e:\n            print \"save kvdb to '%s' failed: %s\" % (db_file, str(e))\n    def _restore_cache():\n        try:\n            _cache.update(pickle.load(open(db_file, 'rb')))\n        except Exception, e:\n            print \"load kvdb from '%s' failed: %s\" % (db_file, str(e))\n    import atexit\n    atexit.register(_save_cache)\n    _restore_cache()\n"
  },
  {
    "path": "dev_server/sae/mail.py",
    "content": "#!/usr/bin/env python\n# -*-coding: utf8 -*-\n\n\"\"\"SAE Mail API\n\nProvides functions for application developers to deliver mail messages \nfor their applications. Currently we only support send mail through SMTP \nasynchronously.\n\nExamle:\n\n1. Send a simple plain-text message.\n\n    from sae.mail import send_mail\n\n    send_mail('recipient@sina.com', 'subject', 'plain text',\n              ('smtp.sina.com', 25, 'me@sina.com', 'password', False))\n\n2. Send a HTML-format message.\n\n    from sae.mail import EmailMessage\n\n    m = EmailMessage()\n    m.to = 'recipient@sina.com'\n    m.subject = 'unforgivable sinner'\n    m.html = '<b>darling, please, please forgive me...</b>'\n    m.smtp = ('smtp.sina.com', 25, 'me@sina.com', 'password', False)\n    m.send()\n\"\"\"\n\n__all__ = ['Error', 'InternalError', 'InvalidAttachmentTypeError', \n           'InvalidRequestError', 'MailTooLargeError', 'MissingBodyError', \n           'MissingRecipientError', 'MissingSMTPError', 'MissingSubjectError',\n           'ServiceUnavailableError', 'MAX_EMAIL_SIZE', 'EmailMessage', \n           'send_mail']\n\nimport base64\nimport json\nimport time\nimport urllib\nimport urllib2\n\nimport core\nimport conf\nimport util\n\nclass Error(Exception):\n    \"\"\"Base-class for all errors in this module\"\"\"\n\nclass InternalError(Error):\n    \"\"\"There was an internal error while sending message, it should be \n    temporary, it problem continues, please contact us\"\"\"\n\nclass InvalidRequestError(Error):\n    \"\"\"The request we send to the mail backend is illengal.\"\"\"\n\nclass MissingRecipientError(Error):\n    \"\"\"No recipient specified in message\"\"\"\n\nclass MissingSubjectError(Error):\n    \"\"\"No subject specified in message\"\"\"\n\nclass MissingBodyError(Error):\n    \"\"\"No body content specified in the message\"\"\"\n\nclass MissingSMTPError(Error):\n    \"\"\"No smtp server configuration is provided.\"\"\"\n\nclass InvalidAttachmentTypeError(Error):\n    \"\"\"The type of the attachment is not permitted.\"\"\"\n\nclass MailTooLargeError(Error):\n    \"\"\"The email is too large, \"\"\"\n\nclass ServiceUnavailableError(Error):\n    \"\"\"The application has reached its service quota or has no permission.\"\"\"\n\n_ERROR_MAPPING = {3: InvalidRequestError, 500: InternalError, 999: InternalError,\n                  999: ServiceUnavailableError}\n\n_MAIL_BACKEND = \"http://mail.sae.sina.com.cn/index.php\"\n\nMAX_EMAIL_SIZE = 1048576 # bytes (1M)\n\nclass EmailMessage(object):\n    \"\"\"Main interface to SAE Mail Service\n    \"\"\"\n    _properties = ['to', 'subject', 'body', 'html', 'attachments', 'smtp', 'from_addr']\n    _ext_to_disposition = {\n        'bmp':  'I', 'css':  'A',\n        'csv':  'A', 'gif':  'I',\n        'htm':  'I', 'html': 'I',\n        'jpeg': 'I', 'jpg':  'I',\n        'jpe':  'I', 'pdf':  'A',\n        'png':  'I', 'rss':  'I',\n        'text': 'A', 'txt':  'A',\n        'asc':  'A', 'diff': 'A',\n        'pot':  'A', 'tiff': 'A',\n        'tif':  'A', 'wbmp': 'I',\n        'ics':  'I', 'vcf':  'I'\n    }\n\n    def __init__(self, **kwargs):\n        \"\"\"Initializer\"\"\"\n        self.initialize(**kwargs)\n\n    def initialize(self, **kwargs):\n        \"\"\"Sets fields of the email message\n        \n        Args:\n          to: The recipient's email address.\n          subject: The subject of the message.\n          body: The content of the message, plain-text only.\n          html: Use this field when you want to send html-encoded message.\n          smtp: This is a five-element tuple of your smtp server's configuration\n            (smtp_host, smtp_port, smtp_username, smtp_password, smtp_tls).\n          attachments: The file attachments of the message, as a list of \n            two-value tuples, one tuple for each attachment. Each tuple contains\n            a filename as the first element, and the file contents as the second\n            element.\n        \"\"\"\n        for name, value in kwargs.iteritems():\n            setattr(self, name, value)\n\n    def send(self):\n        \"\"\"Sends the email message.\n        \n        This method just post the message to the mail delivery queue.\n        \"\"\"\n        message = self._to_proto()\n        #print message\n        self._remote_call(message)\n\n    def check_initialized(self):\n        if not hasattr(self, 'to'):\n            raise MissingRecipientError()\n\n        if not hasattr(self, 'subject'):\n            raise MissingSubjectError()\n\n        if not hasattr(self, 'smtp'):\n            raise MissingSMTPError()\n\n        if not hasattr(self, 'body') and not hasattr(self, 'html'):\n            raise MissingBodyError()\n\n    def _check_email_valid(self, address):\n        if not isinstance(address, basestring):\n            raise TypeError()\n\n        # TODO: validate email address\n\n    def _check_smtp_valid(self, smtp):\n        if not isinstance(smtp, tuple) or len(smtp) != 5:\n            raise TypeError()\n\n    def _check_attachments(self, attachments):\n        for a in attachments:\n            if not isinstance(a, tuple) or len(a) != 2:\n                raise TypeError()\n\n    def __setattr__(self, attr, value):\n        if attr not in self._properties:\n            raise AttributeError(\"'EmailMessage' has no attribute '%s'\" % attr)\n\n        if not value:\n            raise ValueError(\"May not set empty value for '%s'\" % attr)\n\n        if attr == 'to':\n            if isinstance(value, list):\n                for v in value:\n                    self._check_email_valid(v)\n                to = ','.join(value)\n                super(EmailMessage, self).__setattr__(attr, to) \n                return\n\n            self._check_email_valid(value)\n        elif attr == 'smtp':\n            self._check_smtp_valid(value)\n        elif attr == 'attachments':\n            self._check_attachments(value)\n\n        super(EmailMessage, self).__setattr__(attr, value)\n\n    def _to_proto(self):\n        \"\"\"Convert mail mesage to protocol message\"\"\"\n        self.check_initialized()\n\n        args = {'from':          getattr(self, 'from_addr', self.smtp[2]),\n                'to':            self.to,\n                'subject':       self.subject,\n                'smtp_host':     self.smtp[0],\n                'smtp_port':     self.smtp[1],\n                'smtp_username': self.smtp[2],\n                'smtp_password': self.smtp[3],\n                'tls':           self.smtp[4]}\n\n        size = 0\n\n        if hasattr(self, 'body'):\n            args['content'] = self.body\n            args['content_type'] = 'TEXT'\n            size = size + len(self.body)\n        elif hasattr(self, 'html'):\n            args['content'] = self.html\n            args['content_type']  = 'HTML'\n            size = size + len(self.html)\n\n        if hasattr(self, 'attachments'):\n            for attachment in self.attachments:\n                ext = attachment[0].split('.')[-1]\n\n                disposition = self._ext_to_disposition.get(ext)\n                if not disposition:\n                    raise InvalidAttachmentTypeError()\n\n                key = 'attach:' + attachment[0] + ':B:' + disposition\n                args[key] = base64.encodestring(attachment[1])\n\n                size = size + len(attachment[1])\n\n        if size > MAX_EMAIL_SIZE:\n            raise MailTooLargeError()\n\n        message = {'saemail': json.dumps(args)}\n        return message\n\n    def _remote_call(self, message):\n        args = json.loads(message['saemail'])\n\n        # just print the message on console\n        print '[SAE:MAIL] Sending new mail'\n        import pprint\n        pprint.pprint(args)\n\n    def _get_headers(self):\n        access_key = core.get_access_key()\n        secret_key = core.get_secret_key()\n\n        timestamp = time.strftime(\"%Y-%m-%d %H:%M:%S\")\n        msg = 'ACCESSKEY' + access_key + 'TIMESTAMP' + timestamp\n        headers = {'TimeStamp': timestamp,\n                   'AccessKey': access_key,\n                   'Signature': util.get_signature(secret_key, msg)}\n\n        return headers\n\ndef send_mail(to, subject, body, smtp, **kwargs):\n    \"\"\"A shortcut for sending mail\"\"\"\n    kwargs['to'] = to\n    kwargs['subject'] = subject\n    kwargs['body'] = body\n    kwargs['smtp'] = smtp\n    \n    EmailMessage(**kwargs).send()\n\n"
  },
  {
    "path": "dev_server/sae/memcache.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"\nFake client for sae memcached service.\n\nThis client keeps all the data in local memory, and the data will be lost once \nthe process is down.\n\nThis should give you a feel for how this module operates::\n\n    import pylibmc\n    mc = pylibmc.Client()\n\n    mc.set(\"some_key\", \"Some value\")\n    value = mc.get(\"some_key\")\n\n    mc.set(\"another_key\", 3)\n    mc.delete(\"another_key\")\n\n    mc.set(\"key\", \"1\")   # note that the key used for incr/decr must be a string.\n    mc.incr(\"key\")\n    mc.decr(\"key\")\n\nThe standard way to use memcache with a database is like this::\n\n    key = derive_key(obj)\n    obj = mc.get(key)\n    if not obj:\n        obj = backend_api.get(...)\n        mc.set(key, obj)\n\n    # we now have obj, and future passes through this code\n    # will use the object from the cache.\n\nDetailed Documentation\n======================\n\nMore detailed documentation is available in the L{Client} class.\n\"\"\"\n\nimport sys\nimport os\nimport re\nimport time\nimport pickle\n\nSERVER_MAX_KEY_LENGTH = 250\n#  Storing values larger than 1MB requires recompiling memcached.  If you do,\n#  this value can be changed by doing \"memcache.SERVER_MAX_VALUE_LENGTH = N\"\n#  after importing this module.\nSERVER_MAX_VALUE_LENGTH = 1024*1024\n\nclass _Error(Exception):\n    pass\n\nclass _ConnectionDeadError(Exception):\n    pass\n\nclass _CacheEntry(object):\n    \n    def __init__(self, value, flags, expiration):\n        self.value = value\n        self.flags = flags\n        self.created_time = time.time()\n        self.will_expire = expiration != 0\n        self.locked = False\n        self._set_expiration(expiration)\n\n    def _set_expiration(self, expiration):\n        if expiration > (86400 * 30):\n            self.expiration = expiration\n        else:\n            self.expiration = self.created_time + expiration\n\n    def is_expired(self):\n        return self.will_expire and time.time() > self.expiration\n\nclass local(object):\n    pass\n\n_DEAD_RETRY = 30  # number of seconds before retrying a dead server.\n_SOCKET_TIMEOUT = 3  #  number of seconds before sockets timeout.\n\n_cache = {}\n\nclass Client(local):\n    \"\"\"\n    Object representing a pool of memcache servers.\n\n    See L{memcache} for an overview.\n\n    In all cases where a key is used, the key can be either:\n        1. A simple hashable type (string, integer, etc.).\n        2. A tuple of C{(hashvalue, key)}.  This is useful if you want to avoid\n        making this module calculate a hash value.  You may prefer, for\n        example, to keep all of a given user's objects on the same memcache\n        server, so you could use the user's unique id as the hash value.\n\n    @group Setup: __init__, set_servers, forget_dead_hosts, disconnect_all, debuglog\n    @group Insertion: set, add, replace, set_multi\n    @group Retrieval: get, get_multi\n    @group Integers: incr, decr\n    @group Removal: delete, delete_multi\n    @sort: __init__, set_servers, forget_dead_hosts, disconnect_all, debuglog,\\\n           set, set_multi, add, replace, get, get_multi, incr, decr, delete, delete_multi\n    \"\"\"\n    _FLAG_PICKLE  = 1<<0\n    _FLAG_INTEGER = 1<<1\n    _FLAG_LONG    = 1<<2\n    _FLAG_COMPRESSED = 1<<3\n\n    _SERVER_RETRIES = 10  # how many times to try finding a free server.\n\n    # exceptions for Client\n    class MemcachedKeyError(Exception):\n        pass\n    class MemcachedKeyLengthError(MemcachedKeyError):\n        pass\n    class MemcachedKeyCharacterError(MemcachedKeyError):\n        pass\n    class MemcachedKeyNoneError(MemcachedKeyError):\n        pass\n    class MemcachedKeyTypeError(MemcachedKeyError):\n        pass\n    class MemcachedStringEncodingError(Exception):\n        pass\n\n    def __init__(self, servers=[], debug=0, pickleProtocol=0,\n                 pickler=pickle.Pickler, unpickler=pickle.Unpickler,\n                 pload=None, pid=None,\n                 server_max_key_length=SERVER_MAX_KEY_LENGTH,\n                 server_max_value_length=SERVER_MAX_VALUE_LENGTH,\n                 dead_retry=_DEAD_RETRY, socket_timeout=_SOCKET_TIMEOUT,\n                 cache_cas = False):\n        \"\"\"\n        Create a new Client object with the given list of servers.\n\n        @param servers: C{servers} is passed to L{set_servers}.\n        @param debug: whether to display error messages when a server can't be\n        contacted.\n        @param pickleProtocol: number to mandate protocol used by (c)Pickle.\n        @param pickler: optional override of default Pickler to allow subclassing.\n        @param unpickler: optional override of default Unpickler to allow subclassing.\n        @param pload: optional persistent_load function to call on pickle loading.\n        Useful for cPickle since subclassing isn't allowed.\n        @param pid: optional persistent_id function to call on pickle storing.\n        Useful for cPickle since subclassing isn't allowed.\n        @param dead_retry: number of seconds before retrying a blacklisted\n        server. Default to 30 s.\n        @param socket_timeout: timeout in seconds for all calls to a server. Defaults\n        to 3 seconds.\n        @param cache_cas: (default False) If true, cas operations will be\n        cached.  WARNING: This cache is not expired internally, if you have\n        a long-running process you will need to expire it manually via\n        \"client.reset_cas(), or the cache can grow unlimited.\n        @param server_max_key_length: (default SERVER_MAX_KEY_LENGTH)\n        Data that is larger than this will not be sent to the server.\n        @param server_max_value_length: (default SERVER_MAX_VALUE_LENGTH)\n        Data that is larger than this will not be sent to the server.\n        \"\"\"\n        local.__init__(self)\n        self.debug = debug\n        self.cache_cas = cache_cas\n        self.reset_cas()\n\n        # Allow users to modify pickling/unpickling behavior\n        self.server_max_key_length = server_max_key_length\n        self.server_max_value_length = server_max_value_length\n\n        _cache = {}\n\n        self.reset_stats()\n\n    def reset_stats(self):\n        self._get_hits = 0\n        self._get_misses = 0\n        self._cmd_set = 0\n        self._cmd_get = 0\n\n    def reset_cas(self):\n        \"\"\"\n        Reset the cas cache.  This is only used if the Client() object\n        was created with \"cache_cas=True\".  If used, this cache does not\n        expire internally, so it can grow unbounded if you do not clear it\n        yourself.\n        \"\"\"\n        self.cas_ids = {}\n\n    def set_servers(self, servers):\n        \"\"\"\n        Set the pool of servers used by this client.\n\n        @param servers: an array of servers.\n        Servers can be passed in two forms:\n            1. Strings of the form C{\"host:port\"}, which implies a default weight of 1.\n            2. Tuples of the form C{(\"host:port\", weight)}, where C{weight} is\n            an integer weight value.\n        \"\"\"\n        pass\n\n    def get_stats(self, stat_args = None):\n        '''Get statistics from each of the servers.\n\n        @param stat_args: Additional arguments to pass to the memcache\n            \"stats\" command.\n\n        @return: A list of tuples ( server_identifier, stats_dictionary ).\n            The dictionary contains a number of name/value pairs specifying\n            the name of the status field and the string value associated with\n            it.  The values are not converted from strings.\n        '''\n\n        total_bytes= 0\n        for k, e in _cache.iteritems():\n            total_bytes += len(str(e.value))\n\n        curr_items = len(_cache)\n\n        name = '10.67.15.110:9211 (0)'\n        stats = {\n            'bytes': str(total_bytes),\n            'bytes_read': '920852',\n            'bytes_written': '3615514',\n            'cmd_get': str(self._cmd_get),\n            'cmd_set': str(self._cmd_set),\n            'connection_structures': '676',\n            'curr_connections': '3',\n            'curr_items': str(curr_items),\n            'evictions': '0',\n            'get_hits': str(self._get_hits),\n            'get_misses': str(self._get_misses),\n            'limit_maxbytes': '1048576',\n            'pid': '24925',\n            'pointer_size': '64',\n            'rusage_system': '38237.950000',\n            'rusage_user': '53464.940000',\n            'threads': '0',\n            'time': str(int(time.time())),\n            'total_connections': '350149607',\n            'total_items': str(curr_items),\n            'uptime': '2541642',\n            'version': '1.4.5'\n        }\n\n        return [(name, stats),]\n\n    def flush_all(self):\n        'Expire all data currently in the memcache servers.'\n        _cache.clear()\n\n    def debuglog(self, str):\n        if self.debug:\n            sys.stderr.write(\"MemCached: %s\\n\" % str)\n\n    def forget_dead_hosts(self):\n        \"\"\"\n        Reset every host in the pool to an \"alive\" state.\n        \"\"\"\n        pass\n\n    def disconnect_all(self):\n        pass\n\n    def delete_multi(self, keys, time=0, key_prefix=''):\n        '''\n        Delete multiple keys in the memcache doing just one query.\n\n        >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'})\n        >>> mc.get_multi(['key1', 'key2']) == {'key1' : 'val1', 'key2' : 'val2'}\n        1\n        >>> mc.delete_multi(['key1', 'key2'])\n        1\n        >>> mc.get_multi(['key1', 'key2']) == {}\n        1\n\n\n        This method is recommended over iterated regular L{delete}s as it reduces total latency, since\n        your app doesn't have to wait for each round-trip of L{delete} before sending\n        the next one.\n\n        @param keys: An iterable of keys to clear\n        @param time: number of seconds any subsequent set / update commands should fail. Defaults to 0 for no delay.\n        @param key_prefix:  Optional string to prepend to each key when sending to memcache.\n            See docs for L{get_multi} and L{set_multi}.\n\n        @return: 1 if no failure in communication with any memcacheds.\n        @rtype: int\n\n        '''\n        for key in keys:\n            _key = key_prefix + str(key)\n            try:\n                del _cache[_key]\n            except KeyError:\n                pass\n\n        return True\n\n    def delete(self, key):\n        '''Deletes a key from the memcache.\n\n        @return: Nonzero on success.\n        '''\n        if key not in _cache:\n            return False\n        del _cache[key]\n        return True\n\n    def incr(self, key, delta=1):\n        \"\"\"\n        Sends a command to the server to atomically increment the value\n        for C{key} by C{delta}, or by 1 if C{delta} is unspecified.\n        Returns None if C{key} doesn't exist on server, otherwise it\n        returns the new value after incrementing.\n\n        Note that the value for C{key} must already exist in the memcache,\n        and it must be the string representation of an integer.\n\n        >>> mc.set(\"counter\", \"20\")  # returns 1, indicating success\n        1\n        >>> mc.incr(\"counter\")\n        21\n        >>> mc.incr(\"counter\")\n        22\n\n        Overflow on server is not checked.  Be aware of values approaching\n        2**32.  See L{decr}.\n\n        @param delta: Integer amount to increment by (should be zero or greater).\n        @return: New value after incrementing.\n        @rtype: int\n        \"\"\"\n        return self._incrdecr(\"incr\", key, delta)\n\n    def decr(self, key, delta=1):\n        \"\"\"\n        Like L{incr}, but decrements.  Unlike L{incr}, underflow is checked and\n        new values are capped at 0.  If server value is 1, a decrement of 2\n        returns 0, not -1.\n\n        @param delta: Integer amount to decrement by (should be zero or greater).\n        @return: New value after decrementing.\n        @rtype: int\n        \"\"\"\n        return self._incrdecr(\"decr\", key, delta)\n\n    def _incrdecr(self, cmd, key, delta):\n        self.check_key(key)\n\n        if key not in _cache:\n            return False\n\n        if cmd == 'decr':\n            delta = - delta\n\n        value = int(_cache[key].value) + delta\n        if value < 0: value = 0\n        _cache[key].value = value\n\n        return value\n\n    def add(self, key, val, time = 0, min_compress_len = 0):\n        '''\n        Add new key with value.\n\n        Like L{set}, but only stores in memcache if the key doesn't already exist.\n\n        @return: Nonzero on success.\n        @rtype: int\n        '''\n        return self._set(\"add\", key, val, time, min_compress_len)\n\n    def append(self, key, val, time=0, min_compress_len=0):\n        '''Append the value to the end of the existing key's value.\n\n        Only stores in memcache if key already exists.\n        Also see L{prepend}.\n\n        @return: Nonzero on success.\n        @rtype: int\n        '''\n        return self._set(\"append\", key, val, time, min_compress_len)\n\n    def prepend(self, key, val, time=0, min_compress_len=0):\n        '''Prepend the value to the beginning of the existing key's value.\n\n        Only stores in memcache if key already exists.\n        Also see L{append}.\n\n        @return: Nonzero on success.\n        @rtype: int\n        '''\n        return self._set(\"prepend\", key, val, time, min_compress_len)\n\n    def replace(self, key, val, time=0, min_compress_len=0):\n        '''Replace existing key with value.\n\n        Like L{set}, but only stores in memcache if the key already exists.\n        The opposite of L{add}.\n\n        @return: Nonzero on success.\n        @rtype: int\n        '''\n        return self._set(\"replace\", key, val, time, min_compress_len)\n\n    def set(self, key, val, time=0, min_compress_len=0):\n        '''Unconditionally sets a key to a given value in the memcache.\n\n        The C{key} can optionally be an tuple, with the first element\n        being the server hash value and the second being the key.\n        If you want to avoid making this module calculate a hash value.\n        You may prefer, for example, to keep all of a given user's objects\n        on the same memcache server, so you could use the user's unique\n        id as the hash value.\n\n        @return: Nonzero on success.\n        @rtype: int\n        @param time: Tells memcached the time which this value should expire, either\n        as a delta number of seconds, or an absolute unix time-since-the-epoch\n        value. See the memcached protocol docs section \"Storage Commands\"\n        for more info on <exptime>. We default to 0 == cache forever.\n        @param min_compress_len: The threshold length to kick in auto-compression\n        of the value using the zlib.compress() routine. If the value being cached is\n        a string, then the length of the string is measured, else if the value is an\n        object, then the length of the pickle result is measured. If the resulting\n        attempt at compression yeilds a larger string than the input, then it is\n        discarded. For backwards compatability, this parameter defaults to 0,\n        indicating don't ever try to compress.\n        '''\n        return self._set(\"set\", key, val, time, min_compress_len)\n\n\n    def cas(self, key, val, time=0, min_compress_len=0):\n        '''Sets a key to a given value in the memcache if it hasn't been\n        altered since last fetched. (See L{gets}).\n\n        The C{key} can optionally be an tuple, with the first element\n        being the server hash value and the second being the key.\n        If you want to avoid making this module calculate a hash value.\n        You may prefer, for example, to keep all of a given user's objects\n        on the same memcache server, so you could use the user's unique\n        id as the hash value.\n\n        @return: Nonzero on success.\n        @rtype: int\n        @param time: Tells memcached the time which this value should expire,\n        either as a delta number of seconds, or an absolute unix\n        time-since-the-epoch value. See the memcached protocol docs section\n        \"Storage Commands\" for more info on <exptime>. We default to\n        0 == cache forever.\n        @param min_compress_len: The threshold length to kick in\n        auto-compression of the value using the zlib.compress() routine. If\n        the value being cached is a string, then the length of the string is\n        measured, else if the value is an object, then the length of the\n        pickle result is measured. If the resulting attempt at compression\n        yeilds a larger string than the input, then it is discarded. For\n        backwards compatability, this parameter defaults to 0, indicating\n        don't ever try to compress.\n        '''\n        return self._set(\"cas\", key, val, time, min_compress_len)\n\n    def set_multi(self, mapping, time=0, key_prefix='', min_compress_len=0):\n        '''\n        Sets multiple keys in the memcache doing just one query.\n\n        >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'})\n        >>> mc.get_multi(['key1', 'key2']) == {'key1' : 'val1', 'key2' : 'val2'}\n        1\n\n\n        This method is recommended over regular L{set} as it lowers the number of\n        total packets flying around your network, reducing total latency, since\n        your app doesn't have to wait for each round-trip of L{set} before sending\n        the next one.\n\n        @param mapping: A dict of key/value pairs to set.\n        @param time: Tells memcached the time which this value should expire, either\n        as a delta number of seconds, or an absolute unix time-since-the-epoch\n        value. See the memcached protocol docs section \"Storage Commands\"\n        for more info on <exptime>. We default to 0 == cache forever.\n        @param key_prefix:  Optional string to prepend to each key when sending to memcache. Allows you to efficiently stuff these keys into a pseudo-namespace in memcache:\n            >>> notset_keys = mc.set_multi({'key1' : 'val1', 'key2' : 'val2'}, key_prefix='subspace_')\n            >>> len(notset_keys) == 0\n            True\n            >>> mc.get_multi(['subspace_key1', 'subspace_key2']) == {'subspace_key1' : 'val1', 'subspace_key2' : 'val2'}\n            True\n\n            Causes key 'subspace_key1' and 'subspace_key2' to be set. Useful in conjunction with a higher-level layer which applies namespaces to data in memcache.\n            In this case, the return result would be the list of notset original keys, prefix not applied.\n\n        @param min_compress_len: The threshold length to kick in auto-compression\n        of the value using the zlib.compress() routine. If the value being cached is\n        a string, then the length of the string is measured, else if the value is an\n        object, then the length of the pickle result is measured. If the resulting\n        attempt at compression yeilds a larger string than the input, then it is\n        discarded. For backwards compatability, this parameter defaults to 0,\n        indicating don't ever try to compress.\n        @return: List of keys which failed to be stored [ memcache out of memory, etc. ].\n        @rtype: list\n\n        '''\n        self._cmd_set += 1\n\n        for key, value in mapping.iteritems():\n            if isinstance(key, basestring):\n                flags = 0\n            else:\n                flags = 1\n            _key = key_prefix + str(key)\n            self.check_key(_key)\n            _cache[_key] = _CacheEntry(value, flags, time)\n\n        return []\n        \n\n    def _set(self, cmd, key, val, time, min_compress_len = 0):\n        self.check_key(key)\n\n        self._cmd_set += 1\n\n        key_exists = key in _cache\n\n        if ((cmd == 'add' and key_exists) or\n            (cmd == 'replace' and not key_exists) or\n            (cmd == 'prepend' and not key_exists) or\n            (cmd == 'append' and not key_exists)):\n            return False\n\n        if cmd == 'prepend':\n            new_val = val + _cache[key].value\n        elif cmd == 'append':\n            new_val = _cache[key].value + val\n        else:\n            new_val = val\n\n        _cache[key] = _CacheEntry(new_val, 0, time)\n        return True\n\n    def _get(self, cmd, key):\n        self.check_key(key)\n\n        self._cmd_get += 1\n\n        if key in _cache:\n            entry = _cache[key]\n            if not entry.is_expired():\n                self._get_hits += 1\n                return entry.value\n        self._get_misses += 1\n        return None\n\n    def get(self, key):\n        '''Retrieves a key from the memcache.\n\n        @return: The value or None.\n        '''\n        return self._get('get', key)\n\n    def gets(self, key):\n        '''Retrieves a key from the memcache. Used in conjunction with 'cas'.\n\n        @return: The value or None.\n        '''\n        return self._get('gets', key)\n\n    def get_multi(self, keys, key_prefix=''):\n        '''\n        Retrieves multiple keys from the memcache doing just one query.\n\n        >>> success = mc.set(\"foo\", \"bar\")\n        >>> success = mc.set(\"baz\", 42)\n        >>> mc.get_multi([\"foo\", \"baz\", \"foobar\"]) == {\"foo\": \"bar\", \"baz\": 42}\n        1\n        >>> mc.set_multi({'k1' : 1, 'k2' : 2}, key_prefix='pfx_') == []\n        1\n\n        This looks up keys 'pfx_k1', 'pfx_k2', ... . Returned dict will just have unprefixed keys 'k1', 'k2'.\n        >>> mc.get_multi(['k1', 'k2', 'nonexist'], key_prefix='pfx_') == {'k1' : 1, 'k2' : 2}\n        1\n\n        get_mult [ and L{set_multi} ] can take str()-ables like ints / longs as keys too. Such as your db pri key fields.\n        They're rotored through str() before being passed off to memcache, with or without the use of a key_prefix.\n        In this mode, the key_prefix could be a table name, and the key itself a db primary key number.\n\n        >>> mc.set_multi({42: 'douglass adams', 46 : 'and 2 just ahead of me'}, key_prefix='numkeys_') == []\n        1\n        >>> mc.get_multi([46, 42], key_prefix='numkeys_') == {42: 'douglass adams', 46 : 'and 2 just ahead of me'}\n        1\n\n        This method is recommended over regular L{get} as it lowers the number of\n        total packets flying around your network, reducing total latency, since\n        your app doesn't have to wait for each round-trip of L{get} before sending\n        the next one.\n\n        See also L{set_multi}.\n\n        @param keys: An array of keys.\n        @param key_prefix: A string to prefix each key when we communicate with memcache.\n            Facilitates pseudo-namespaces within memcache. Returned dictionary keys will not have this prefix.\n        @return:  A dictionary of key/value pairs that were available. If key_prefix was provided, the keys in the retured dictionary will not have it present.\n\n        '''\n        self._cmd_get += 1\n\n        retvals = {}\n        for key in keys:\n            _key = key_prefix + str(key)\n            try:\n                entry = _cache[_key]\n            except KeyError:\n                self._get_misses += 1\n                continue\n\n            if entry.is_expired():\n                self._get_misses += 1\n                continue\n            if entry.flags ==  1:\n                key = int(key)\n            retvals[key] = entry.value\n            self._get_hits += 1\n\n        return retvals\n\n    def check_key(self, key, key_extra_len=0):\n        \"\"\"Checks sanity of key.  Fails if:\n            Key length is > SERVER_MAX_KEY_LENGTH (Raises MemcachedKeyLength).\n            Contains control characters  (Raises MemcachedKeyCharacterError).\n            Is not a string (Raises MemcachedStringEncodingError)\n            Is an unicode string (Raises MemcachedStringEncodingError)\n            Is not a string (Raises MemcachedKeyError)\n            Is None (Raises MemcachedKeyError)\n        \"\"\"\n        if isinstance(key, tuple): key = key[1]\n        if not key:\n            raise Client.MemcachedKeyNoneError(\"Key is None\")\n        if isinstance(key, unicode):\n            raise Client.MemcachedStringEncodingError(\n                    \"Keys must be str()'s, not unicode.  Convert your unicode \"\n                    \"strings using mystring.encode(charset)!\")\n        if not isinstance(key, str):\n            raise Client.MemcachedKeyTypeError(\"Key must be str()'s\")\n\n        if isinstance(key, basestring):\n            if self.server_max_key_length != 0 and \\\n                len(key) + key_extra_len > self.server_max_key_length:\n                raise Client.MemcachedKeyLengthError(\"Key length is > %s\"\n                         % self.server_max_key_length)\n            for char in key:\n                if ord(char) < 33 or ord(char) == 127:\n                    raise Client.MemcachedKeyCharacterError(\n                            \"Control characters not allowed\")\n\ndef _doctest():\n    import doctest, memcache\n    servers = [\"127.0.0.1:11211\"]\n    mc = Client(servers, debug=1)\n    globs = {\"mc\": mc}\n    return doctest.testmod(memcache, globs=globs)\n\nif __name__ == \"__main__\":\n    failures = 0\n    print \"Testing docstrings...\"\n    _doctest()\n    print \"Running tests:\"\n    print\n    serverList = [[\"127.0.0.1:11211\"]]\n    if '--do-unix' in sys.argv:\n        serverList.append([os.path.join(os.getcwd(), 'memcached.socket')])\n\n    for servers in serverList:\n        mc = Client(servers, debug=1)\n\n        def to_s(val):\n            if not isinstance(val, basestring):\n                return \"%s (%s)\" % (val, type(val))\n            return \"%s\" % val\n        def test_setget(key, val):\n            global failures\n            print \"Testing set/get {'%s': %s} ...\" % (to_s(key), to_s(val)),\n            mc.set(key, val)\n            newval = mc.get(key)\n            if newval == val:\n                print \"OK\"\n                return 1\n            else:\n                print \"FAIL\"; failures = failures + 1\n                return 0\n\n\n        class FooStruct(object):\n            def __init__(self):\n                self.bar = \"baz\"\n            def __str__(self):\n                return \"A FooStruct\"\n            def __eq__(self, other):\n                if isinstance(other, FooStruct):\n                    return self.bar == other.bar\n                return 0\n\n        test_setget(\"a_string\", \"some random string\")\n        test_setget(\"an_integer\", 42)\n        if test_setget(\"long\", long(1<<30)):\n            print \"Testing delete ...\",\n            if mc.delete(\"long\"):\n                print \"OK\"\n            else:\n                print \"FAIL\"; failures = failures + 1\n            print \"Checking results of delete ...\"\n            if mc.get(\"long\") == None:\n                print \"OK\"\n            else:\n                print \"FAIL\"; failures = failures + 1\n        print \"Testing get_multi ...\",\n        print mc.get_multi([\"a_string\", \"an_integer\"])\n\n        #  removed from the protocol\n        #if test_setget(\"timed_delete\", 'foo'):\n        #    print \"Testing timed delete ...\",\n        #    if mc.delete(\"timed_delete\", 1):\n        #        print \"OK\"\n        #    else:\n        #        print \"FAIL\"; failures = failures + 1\n        #    print \"Checking results of timed delete ...\"\n        #    if mc.get(\"timed_delete\") == None:\n        #        print \"OK\"\n        #    else:\n        #        print \"FAIL\"; failures = failures + 1\n\n        print \"Testing get(unknown value) ...\",\n        print to_s(mc.get(\"unknown_value\"))\n\n        f = FooStruct()\n        test_setget(\"foostruct\", f)\n\n        print \"Testing incr ...\",\n        x = mc.incr(\"an_integer\", 1)\n        if x == 43:\n            print \"OK\"\n        else:\n            print \"FAIL\"; failures = failures + 1\n\n        print \"Testing decr ...\",\n        x = mc.decr(\"an_integer\", 1)\n        if x == 42:\n            print \"OK\"\n        else:\n            print \"FAIL\"; failures = failures + 1\n        sys.stdout.flush()\n\n        # sanity tests\n        print \"Testing sending spaces...\",\n        sys.stdout.flush()\n        try:\n            x = mc.set(\"this has spaces\", 1)\n        except Client.MemcachedKeyCharacterError, msg:\n            print \"OK\"\n        else:\n            print \"FAIL\"; failures = failures + 1\n\n        print \"Testing sending control characters...\",\n        try:\n            x = mc.set(\"this\\x10has\\x11control characters\\x02\", 1)\n        except Client.MemcachedKeyCharacterError, msg:\n            print \"OK\"\n        else:\n            print \"FAIL\"; failures = failures + 1\n\n        print \"Testing using insanely long key...\",\n        try:\n            x = mc.set('a'*SERVER_MAX_KEY_LENGTH, 1)\n        except Client.MemcachedKeyLengthError, msg:\n            print \"FAIL\"; failures = failures + 1\n        else:\n            print \"OK\"\n        try:\n            x = mc.set('a'*SERVER_MAX_KEY_LENGTH + 'a', 1)\n        except Client.MemcachedKeyLengthError, msg:\n            print \"OK\"\n     \n"
  },
  {
    "path": "dev_server/sae/sae_signature.py",
    "content": "\nimport os\nimport base64\nimport hmac\nimport hashlib\n\ndef get_signature(key, msg):\n    h = hmac.new(key, msg, hashlib.sha256)\n    return base64.b64encode(h.digest())\n\ndef get_signatured_headers(headers):\n    \"\"\"Given a list of headers, return a signatured dict\n    Becareful of the order of headers when signaturing\n    \"\"\"\n    d = {}\n    msg = ''\n    for k, v in headers:\n        d[k] = v\n        msg += k + v\n\n    secret = os.environ.get('SECRET_KEY', '')\n    d['Signature'] = get_signature(secret, msg)\n    return d\n"
  },
  {
    "path": "dev_server/sae/storage.py",
    "content": "#!/usr/bin/env python\n# -*-coding: utf8 -*-\n\n\"\"\" Dummy SAE Storage API\n\"\"\"\n\nimport os\nimport errno\nimport mimetypes\nfrom datetime import datetime\nfrom urllib import quote as _quote\n\nDEFAULT_API_URL = 'https://api.sinas3.com'\nACCESS_KEY = SECRET_KEY = APP_NAME = 'x'\nDEFAULT_API_VERSION = 'v1'\nDEFAULT_RESELLER_PREFIX = 'SAE_'\n\nclass Error(Exception): pass\n\nclass AttrDict(dict):\n    def __getattr__(self, name):\n        try:\n            return self[name]\n        except KeyError:\n            raise AttributeError(name)\n\ndef q(value, safe='/'):\n    value = encode_utf8(value)\n    if isinstance(value, str):\n        return _quote(value, safe)\n    else:\n        return value\n\ndef encode_utf8(value):\n    if isinstance(value, unicode):\n        value = value.encode('utf8')\n    return value\n\nclass Bucket:\n    def __init__(self, bucket, conn=None):\n        self.conn = conn if conn else Connection()\n        self.bucket = bucket\n\n    _s = \"\"\"\ndef %s(self, *args, **kws):\n    return self.conn.%s_bucket(self.bucket, *args, **kws)\n\"\"\"\n    for _m in ('put', 'post', 'stat', 'delete', 'list'):\n        exec _s % (_m, _m)\n\n    _s = \"\"\"\ndef %s(self, *args, **kws):\n    return self.conn.%s(self.bucket, *args, **kws)\n\"\"\"\n    for _m in ('get_object', 'get_object_contents', 'put_object',\n               'post_object', 'stat_object', 'delete_object',\n               'generate_url'):\n        exec _s % (_m, _m)\n\n    del _m, _s\n\nclass Connection(object):\n    def __init__(self, accesskey=ACCESS_KEY, secretkey=SECRET_KEY,\n                 account=APP_NAME, retries=3, backoff=0.5,\n                 api_url=DEFAULT_API_URL,\n                 api_version = DEFAULT_API_VERSION,\n                 reseller_prefix=DEFAULT_RESELLER_PREFIX,\n                 bucket_class=Bucket):\n        if accesskey is None or secretkey is None or account is None:\n            raise TypeError(\n                '`accesskey` or `secretkey` or `account` is missing')\n        self.bucket_class = bucket_class\n\n    def list_bucket(self, bucket, prefix=None, delimiter=None,\n                    path=None, limit=10000, marker=None):\n        if path:\n            prefix = path\n            delimiter = '/'\n        objs = []\n        pth = os.path.normpath(self._get_storage_path(bucket))\n        for dpath, dnames, fnames in os.walk(pth):\n            rpath = dpath[len(pth)+1:]\n            objs.extend([os.path.join(rpath, f) for f in fnames])\n        last_subdir = None\n        startpos = len(prefix) if delimiter and prefix else 0\n        for obj in objs:\n            if prefix:\n                if not obj.startswith(prefix):\n                    continue\n            if delimiter:\n                endpos = obj.find(delimiter, startpos)\n                if endpos != -1:\n                    subdir = obj[:endpos+1]\n                    if subdir != last_subdir:\n                        item = AttrDict()\n                        item['bytes'] = None\n                        item['content_type'] = None\n                        item['hash'] = None\n                        item['last_modified'] = None\n                        item['name'] = subdir\n                        yield item\n                        last_subdir = subdir\n                    continue\n            item = AttrDict()\n            item['bytes'] = '12'\n            item['content_type'] = 'application/octet-stream'\n            item['hash'] = 'x' * 40\n            item['last_modified'] = '2013-05-23T03:01:59.051030'\n            item['name'] = obj\n            yield item\n\n    def stat_bucket(self, bucket):\n        attrs = AttrDict()\n        attrs['acl'] = '.r:*'\n        attrs['bytes'] = '10240'\n        attrs['objects'] = '10240'\n        attrs['metadata'] = {}\n        return attrs\n\n    def get_bucket(self, bucket):\n        return self.bucket_class(bucket)\n\n    def put_bucket(self, bucket, acl=None, metadata=None):\n        path = self._get_storage_path(bucket)\n        try:\n            os.mkdir(path)\n        except OSError, e:\n            if e.errno != errno.EEXIST:\n                raise Error(500, str(e))\n\n    def post_bucket(self, bucket, acl=None, metadata=None):\n        pass\n\n    def delete_bucket(self, bucket):\n        path = self._get_storage_path(bucket)\n        try:\n            os.rmdir(path)\n        except OSError, e:\n            if e.errno == errno.ENOENT:\n                raise Error(404, 'Not Found')\n            elif e.errno == errno.ENOTEMPTY:\n                raise Error(409, 'Confict')\n            else:\n                raise Error(500, str(e))\n\n    def get_object(self, bucket, obj, chunk_size=None):\n        return self.stat_object(bucket, obj), \\\n                self.get_object_contents(bucket, obj, chunk_size)\n\n    def get_object_contents(self, bucket, obj, chunk_size=None):\n        fname = self._get_storage_path(bucket, obj)\n        try:\n            resp = open(fname, 'rb')\n        except IOError, e:\n            if e.errno == errno.ENOENT:\n                raise Error(404, 'Not Found')\n            else:\n                raise Error(500, str(e))\n        if chunk_size:\n            def _body():\n                buf = resp.read(chunk_size)\n                while buf:\n                    yield buf\n                    buf = resp.read(chunk_size)\n            return _body()\n        else:\n            return resp.read()\n\n    def stat_object(self, bucket, obj):\n        fname = self._get_storage_path(bucket, obj)\n        try:\n            st = os.stat(fname)\n        except OSError, e:\n            if e.errno == errno.ENOENT:\n                raise Error(404, 'Not Found')\n            else:\n                raise Error(500, str(e))\n        attrs = AttrDict()\n        attrs['bytes'] = str(st.st_size)\n        attrs['hash'] = 'x'*40\n        attrs['last_modified'] = datetime.utcfromtimestamp(\n                float(st.st_mtime)).isoformat()\n        attrs['content_type'] = mimetypes.guess_type(obj)[0] or \\\n                                'application/octet-stream'\n        attrs['content_encoding'] = None\n        attrs['timestamp'] = str(st.st_mtime)\n        attrs['metadata'] = {}\n        return attrs\n\n    def put_object(self, bucket, obj, contents,\n                   content_type=None, content_encoding=None,\n                   metadata=None):\n        fname = self._get_storage_path(bucket, obj)\n        if hasattr(contents, 'read'):\n            contents = contents.read()\n        try:\n            os.makedirs(os.path.dirname(fname))\n        except OSError, e:\n            if e.errno != errno.EEXIST:\n                raise Error(500, str(e))\n        try:\n            open(fname, 'wb').write(contents)\n        except IOError, e:\n            raise Error(500, str(e))\n\n    def post_object(self, bucket, obj,\n                    content_type=None, content_encoding=None,\n                    metadata=None):\n        pass\n\n    def generate_url(self, bucket, obj):\n        return 'http://%s/stor-stub/%s/%s' % \\\n            (os.environ['HTTP_HOST'], bucket, q(obj))\n\n    def delete_object(self, bucket, obj):\n        fname = self._get_storage_path(bucket, obj)\n        try:\n            os.unlink(fname)\n        except OSError, e:\n            if e.errno == errno.ENOENT:\n                raise Error(404, 'Not Found')\n        bname = self._get_storage_path(bucket)\n        fname = os.path.dirname(fname)\n        while fname and len(fname) > len(bname):\n            try:\n                os.rmdir(fname)\n            except OSError, e:\n                if e.errno == errno.ENOTEMPTY:\n                    break\n                else:\n                    raise Error(500, str(e))\n            fname = os.path.dirname(fname)\n\n    _STORAGE_PATH = os.environ.get('sae.storage.path')\n    def _get_storage_path(self, *args):\n        if not self._STORAGE_PATH:\n            raise RuntimeError(\n                \"Please specify --storage-path in the command line\")\n        if not os.path.isdir(self._STORAGE_PATH):\n            raise RuntimeError(\n                \"'%s' directory does not exists\" % self._STORAGE_PATH)\n        return os.path.join(self._STORAGE_PATH, *args)\n"
  },
  {
    "path": "dev_server/sae/taskqueue.py",
    "content": "#!/usr/bin/env python\n# -*-coding: utf8 -*-\n\n\"\"\"Task Queue API\nTaskQueue is a distributed task queue service provided by SAE for developers as\na simple way to execute asynchronous user tasks.\n\nExample:\n\n1. Add a GET task.\n    \n    from sae.taskqueue import Task, TaskQueue\n\n    queue = TaskQueue('queue_name')\n    queue.add(Task(\"/tasks/cd\"))\n\n2. Add a POST task.\n\n    queue.add(Task(\"/tasks/strip\", \"postdata\"))\n\n3. Add a bundle of tasks.\n\n    tasks = [Task(\"/tasks/grep\", d) for d in datas]\n    queue.add(tasks)\n\n4. A simple way to add task.\n\n    from sae.taskqueue import add_task\n    add_task('queue_name', '/tasks/fsck', 'postdata')\n\"\"\"\n\n__all__ = ['Error', 'InternalError', 'InvalidTaskError', \n           'PermissionDeniedError', 'TaskQueueNotExistsError', \n           'TooManyTasksError', 'add_task', 'Task', 'TaskQueue']\n\nimport os\nimport time\nimport json\nimport urllib\nimport urllib2\nimport urlparse\nimport base64\n\nimport util\nimport const\n\nclass Error(Exception):\n    \"\"\"Base-class for all exception in this module\"\"\"\n\nclass InvalidTaskError(Error):\n    \"\"\"The task's url, payload, or options is invalid\"\"\"\n\nclass InternalError(Error):\n    \"\"\"There was an internal error while accessing this queue, it should be \n    temporary, it problem continues, please contact us\"\"\"\n\nclass PermissionDeniedError(Error):\n    \"\"\"The requested operation is not allowed for this app\"\"\"\n\nclass TaskQueueNotExistsError(Error):\n    \"\"\"The specified task queue does not exist\"\"\"\n\nclass TooManyTasksError(Error):\n    \"\"\"Either the taskqueue is Full or the space left's not enough\"\"\"\n\n_ERROR_MAPPING = {\n    1: PermissionDeniedError, 3: InvalidTaskError, 10: TaskQueueNotExistsError,\n    11: TooManyTasksError, 500: InternalError, #999: UnknownError,\n    #403: Permission denied or out of quota \n}\n\n_TASKQUEUE_BACKEND = 'http://taskqueue.sae.sina.com.cn/index.php'\n\nclass Task:\n\n    _default_netloc = 'http://' + os.environ['HTTP_HOST']\n\n    def __init__(self, url, payload = None, **kwargs):\n        \"\"\"Initializer.\n\n        Args:\n          url: URL where the taskqueue daemon should handle this task.\n          payload: Optinal, if provided, the taskqueue daemon will take this \n            task as a POST task and |payload| as POST data.\n          delay: Delay the execution of the task for certain second(s). Up to\n            600 seconds.\n          prior: If set to True, the task will be add to the head of the queue.\n\n        Raises:\n          InvalidTaskError: if there's a unrecognized argument.\n        \"\"\"\n        self.info = {}\n        if url.startswith('http://'):\n            self.info['url'] = url\n        else:\n            self.info['url'] = urlparse.urljoin(self._default_netloc, url)\n        if payload:\n            self.info['postdata'] = base64.b64encode(payload)\n                \n        for k, v in kwargs.iteritems():\n            if k == 'delay':\n                self.info['delay'] = v\n            elif k == 'prior':\n                self.info['prior'] = v\n            else:\n                raise InvalidTaskError()\n\n    def extract_params(self):\n        return self.info\n\nclass TaskQueue:\n\n    def __init__(self, name, auth_token=None):\n        \"\"\"Initializer.\n\n        Args:\n          name: The name of the taskqueue.\n          auth_token: Optional, a two-element tuple (access_key, secretkey_key),\n            useful when you want to access other application's taskqueue.\n        \"\"\"\n        self.name = name\n\n        if auth_token: \n            self.accesskey_key, self.secret_key = auth_token\n        else:\n            self.access_key = const.ACCESS_KEY\n            self.secret_key = const.SECRET_KEY\n\n    def add(self, task):\n        \"\"\"Add task to the task queue\n\n        Args:\n          task: The task to be added, it can be a single Task, or a list of \n            Tasks.\n        \"\"\"\n        try:\n            tasks = list(iter(task))\n        except TypeError:\n            tasks = [task]\n\n        task_args = {}\n        task_args['name'] = self.name\n        task_args['queue'] = []\n        for t in tasks:\n            task_args['queue'].append(t.extract_params())\n\n        #print task_args\n        args = [('taskqueue', json.dumps(task_args))]\n\n        return self._remote_call(args)\n\n    def size(self):\n        \"\"\"Query for how many task is left(not executed) in the queue. \"\"\"\n        args = []\n        args.append(('act', 'curlen'))\n        args.append(('params', json.dumps({'name': self.name})))\n        return int(self._remote_call(args))\n\n    def _remote_call(self, args):\n        args_dict = dict(args)\n\n        command = args_dict.get('act')\n        if command == 'curlen':\n            return \"0\"\n\n        tasks = json.loads(args_dict['taskqueue'])['queue']\n        for t in tasks:\n            url = t['url']\n            payload = t.get('postdata')\n\n            if payload:\n                payload = base64.b64decode(payload)\n            print '[SAE:TASKQUEUE] Add task:', url, payload\n\n            #try:\n            #    # Try to make a sync call.\n            #    rep = urllib2.urlopen(url, payload, 5)\n            #    print rep.read()\n            #except:\n            #    import traceback\n            #    print 'TASKQUEUE_ERROR:', t    \n            #    traceback.print_exc()\n\n        return True\n\n    def _get_headers(self):\n        timestamp = time.strftime(\"%Y-%m-%d %H:%M:%S\")\n        msg = 'ACCESSKEY' + self.access_key + 'TIMESTAMP' + timestamp\n        headers = {'TimeStamp': timestamp,\n                   'AccessKey': self.access_key,\n                   'Signature': util.get_signature(self.secret_key, msg)}\n\n        return headers\n\ndef add_task(queue_name, url, payload=None, **kws):\n    \"\"\"A shortcut for adding task\n    \n    Args:\n      queue_name: The queue's name of which you want the task be added to.\n      url: URL where the taskqueue daemon should handle this task.\n      payload: The post data if you want to do a POST task.\n    \"\"\"\n    TaskQueue(queue_name).add(Task(url, payload, **kws))\n"
  },
  {
    "path": "dev_server/sae/util.py",
    "content": "\nfrom sae_signature import get_signature, get_signatured_headers\n\ndef half_secret(d, k):\n    \"\"\"Hidden part of the secret\"\"\"\n    l = len(d[k])\n    if l > 2:\n        d[k] = d[k][:2] + '*' * (l - 2)\n    else:\n        d[k] = '*' * l\n\ndef protect_secret(d):\n    for k, v in d.items():\n        if 'KEY' in k:\n            half_secret(d, k)\n\nimport os.path\n\ndef search_file_bottom_up(name):\n    curdir = os.getcwd()\n\n    while True:\n        path = os.path.join(curdir, name)\n        if os.path.isfile(path):\n            return curdir\n        _curdir = os.path.dirname(curdir)\n        if _curdir == curdir:\n            return None\n        curdir = _curdir\n\n"
  },
  {
    "path": "dev_server/saecloud",
    "content": "#!/usr/bin/env python\n\nimport os\nimport sys\nimport yaml \nimport json\nimport shutil\nimport filecmp\nimport subprocess\nimport argparse\nimport os.path\nimport base64\nimport hmac\nimport hashlib\nimport urllib\nimport urllib2\n\nfrom sae.util import search_file_bottom_up\n\nUPLOAD_SERVER = 'http://upload.sae.sina.com.cn'\nDEPLOY_SERVER = 'http://deploy.sae.sina.com.cn'\nSVN_SERVER = 'https://svn.sinaapp.com/'\nLOCAL_CACHE_DIR = os.path.join(os.path.expanduser('~'),'.saecloud')\n\nVERSION = '0.0.1'\nverbose = False\n\ndef version(args):\n    print \"SAE command line v%s\" % VERSION\n\ndef run(*args):\n    # FIXME: Check the return code please\n    if verbose:\n        print '+', ' '.join(args)\n        return subprocess.call(args)\n    else:\n        return subprocess.call(args, stdout=open(os.devnull, 'w'))\n\ndef _get_svn_opts(args):\n    opts = []\n    for opt in ['username', 'password']:\n        value = getattr(args, opt)\n        if value:\n            opts.append('--' + opt)\n            opts.append(value)\n    return opts\n\ndef deploy(args):\n    \"\"\"Deploy local source to server\n\n    Deploy code in source directory to sae server, by default source is current\n    directory, version number is set in config.yaml. \n\n    \"\"\"\n    source = args.dir \n    opts = _get_svn_opts(args)\n    cache = LOCAL_CACHE_DIR\n\n    if source is None:\n        source = search_file_bottom_up('config.yaml')\n        if source is None:\n            print >>sys.stderr,     \\\n                'error: Not an app directory(or any of the parent directories)'\n            return\n\n    conf_file = os.path.join(source, 'config.yaml')\n    try:\n        conf =  yaml.load(open(conf_file))\n    except:\n        print >>sys.stderr, 'error: Failed to load config.yaml'\n        return\n\n    name =  conf['name']\n    version = conf['version']\n\n    print 'Deploying http://%s.%s.sinaapp.com' % (version, name)\n    print 'Updating cache'\n    name = str(name)\n    path = os.path.join(cache, name)\n    if not os.path.exists(path):\n        url = SVN_SERVER + name\n        run('svn', 'checkout', url, path, *opts)\n    else:\n        run('svn', 'cleanup', path)\n        run('svn', 'update', path, '-q')\n\n    print 'Finding changes'\n    modified = False\n    vpath = os.path.join(path, str(version))\n    if os.path.exists(vpath):\n        q = ['',]\n        while len(q):\n            part = q.pop(0)\n            s = os.path.join(source, part)\n            t = os.path.join(vpath, part)\n            dc = filecmp.dircmp(s, t, ['.svn'])\n\n            # New files\n            for f in dc.left_only:\n                if f.startswith('.'): \n                    continue\n                d1 = os.path.join(s, f)\n                d2 = os.path.join(t, f)\n                if os.path.isdir(d1):\n                    shutil.copytree(d1, d2)\n                else:\n                    shutil.copy2(d1, d2)\n                run('svn', 'add', d2, '-q')\n                modified = True\n\n            # Deleted files\n            for f in dc.right_only:\n                if f.startswith('.'): \n                    continue\n                d = os.path.join(t, f)\n                if os.path.isdir(d):\n                    shutil.rmtree(d)\n                else:\n                    os.unlink(d)\n                run('svn', 'delete', d, '-q')\n                modified = True\n\n            # Modified files\n            for f in dc.diff_files:\n                if f.startswith('.'): \n                    continue\n                d1 = os.path.join(s, f)\n                d2 = os.path.join(t, f)\n                shutil.copy2(d1, d2)\n                modified = True\n\n            subdirs = filter(lambda x: not x.startswith('.'), dc.common_dirs)\n            q.extend([os.path.join(part, d) for d in subdirs])\n    else:\n        # New version\n        shutil.copytree(source, vpath, ignore=shutil.ignore_patterns('.*'))\n        run('svn', 'add', vpath, '-q')\n        modified = True\n\n    if not modified:\n        print 'No changes found'\n    print 'Deploying to server... ',\n    sys.stdout.flush()\n    run('svn', 'commit', path, '-mx')\n    print 'done'\n\ndef export(args):\n    \"\"\"Export source from sae server\n\n    Export source currently deployed on the sae server to currently directory.\n    Version 1 will be used unless you have specified a version number, also, \n    you can specify your svn username and password just as `saecloud depoly`\n\n    \"\"\"\n    url = SVN_SERVER + args.app + '/' + args.version\n    print 'Exporting to', args.app\n    opts = _get_svn_opts(args)\n    run('svn', 'export', url, args.app, *opts)\n\ndef install(args):\n    # If we are in an app directory, try to install the package in the\n    # standard directory.\n    parent_dir = search_file_bottom_up('config.yaml') or os.getcwd()\n    dest = os.path.join(parent_dir, 'site-packages')\n\n    if not os.path.exists(dest):\n        os.mkdir(dest)\n\n    import tempfile\n    tmpdir = tempfile.gettempdir()\n    argv = ['install', '-I', '--allow-all-external',\n            '--install-option=--install-lib=%s' % dest,\n            '--install-option=--install-data=%s' % dest,\n            '--install-option=--install-scripts=%s' % tmpdir]\n    # only compile if it is python2.7.3\n    import imp\n    magic = imp.get_magic()[:2]\n    if magic != '\\x03\\xf3':\n        argv.append('--install-option=--no-compile')\n    if args.requirement:\n        argv.extend(['-r', args.requirement[0]])\n    argv.extend(args.package)\n\n    # In virtualenv, the install command will remove the old installed\n    # distribution, patch to skip it.\n    try:\n        def _(*arg, **kws):\n            pass\n        import pip.req\n        pip.req.InstallRequirement.uninstall = _\n        pip.req.InstallRequirement.commit_uninstall = _\n    except:\n        pass\n\n    import pip\n    pip.main(argv)\n\n    for f in os.listdir(dest):\n        pth = os.path.join(dest, f)\n        if os.path.isfile(pth) and f.endswith('.egg'):\n            print 'uncompress: %s' % f\n            import zipfile\n            zf = zipfile.ZipFile(pth)\n            zf.extractall(dest)\n            zf.close()\n            os.unlink(pth)\n\nHISTORY_PATH = os.path.join(LOCAL_CACHE_DIR, 'shell.hist')\n\ndef shell(args):\n    app_name = args.app\n    if app_name is None:\n        app_root = search_file_bottom_up('config.yaml')\n        try:\n            path = os.path.join(app_root, 'config.yaml')\n            app_name = yaml.load(open(path))['name']\n        except Exception:\n            pass\n    if app_name is None:\n        print >>sys.stderr, 'error: No app is specified'\n        return\n\n    api_url = 'http://%s.sinaapp.com/_sae/shell/rpc' % app_name\n\n    import time\n    session = None\n    password = args.password and args.password[0]\n\n    def _rpc(op, **kwargs):\n        req = json.dumps({'op': op,\n                          'session': session,\n                          'password': password,\n                          'kwargs': kwargs})\n        body = urllib2.urlopen(api_url, req).read()\n        return body\n\n    import code\n\n    try:\n        import readline, atexit\n        readline.parse_and_bind('tab: complete')\n        atexit.register(lambda: readline.write_history_file(HISTORY_PATH))\n        if os.path.exists(HISTORY_PATH):\n            readline.read_history_file(HISTORY_PATH)\n    except ImportError:\n        pass\n\n    try:\n        session, python_version = _rpc('open').split(' ', 1)\n    except Exception:\n        print >>sys.stderr, \\\n            'error: connect with %s\\'s ShellMiddleware' % app_name\n        return\n\n    banner = '''Python %s\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n''' % python_version\n\n    class _InteractiveConsole(code.InteractiveConsole):\n        def runsource(self, source, filename=\"<input>\", symbol=\"single\"):\n            try:\n                if self.compile(source, filename, symbol) is None:\n                    # Imcomplete code\n                    return True\n            except (OverflowError, SyntaxError, ValueError):\n                # Syntax error\n                self.showsyntaxerror(filename)\n                return False\n\n            try:\n                self.write(_rpc('run',\n                                source=source, filename=filename, mode=symbol))\n            except Exception:\n                self.write('error: execute code on server\\n')\n            return False\n\n    _InteractiveConsole().interact(banner)\n\ndef main():\n\n    parser = argparse.ArgumentParser(prog=os.path.basename(sys.argv[0]))\n    parser.set_defaults(verbose=False)\n\n    subparsers = parser.add_subparsers(help='sub commands')\n\n    credentials = argparse.ArgumentParser(add_help=False)\n    credentials.add_argument('-u', '--username', help='repo username')\n    credentials.add_argument('-p', '--password', help='repo password')\n    credentials.add_argument('-v', '--verbose', dest='verbose', action='store_true', \n                             help='show lowlevel repo operations')\n\n    p = subparsers.add_parser('export', parents=[credentials],\n                              help='export source code to local directory')\n    p.add_argument('app', help='application name')\n    p.add_argument('version', nargs='?', default='1', \n                   help='which version to export, default to 1')\n    p.set_defaults(func=export)\n\n    p = subparsers.add_parser('deploy', parents=[credentials], \n                              help='deploy source directory to SAE')\n    p.add_argument('dir', nargs='?', default=None,\n                   help='the source code directory to deploy, default to current dir')\n    p.set_defaults(func=deploy)\n\n    p = subparsers.add_parser('install',\n                              help='helper to install packages for SAE application')\n    p.add_argument('package', nargs='*', help='package name to install')\n    p.add_argument('-r', '--requirement', nargs=1,\n                   help='install all the packages listed in the given requirements file')\n    p.set_defaults(func=install)\n\n    p = subparsers.add_parser('version', help='show version info')\n    p.set_defaults(func=version)\n\n    p = subparsers.add_parser('shell', help='remote python shell runs on SAE')\n    p.add_argument('app', nargs='?', default=None, help='which app\\'s shell to run')\n    p.add_argument('-p', '--password', nargs=1,\n                   help='password you set in the ShellMiddleware')\n    p.set_defaults(func=shell)\n\n    args = parser.parse_args()\n    global verbose\n    if args.verbose: verbose = True\n\n    args.func(args)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "dev_server/setup.py",
    "content": "\nimport os.path\nfrom setuptools import setup, find_packages\n\nVERSION = '1.3.6'\n\nscripts = ['dev_server.py', 'saecloud', 'cloudsql.py']\n\nif os.name == 'nt':\n    # XXX: shebang does not work on windows\n    BAT = 'saecloud.bat'\n    f = os.path.join(os.path.dirname(__file__), BAT)\n    open(f, 'w').write('@python \"%~dp0\\saecloud\" %*')\n    scripts.append(BAT)\n\nsetup(\n    name = 'sae-python-dev',\n    version = VERSION,\n    author = 'SAE Python Team',\n    author_email = 'saemail@sina.cn',\n    description = ('SAE Python development server'),\n    install_requires = [\n        'Werkzeug',\n        'pip',\n        'PyYAML',\n        'argparse',\n        # XXX: The latest grizzled-python package is broken\n        'grizzled-python==1.0.1',\n        'sqlcmd',\n        'prettytable',\n        ],\n    platforms='any',\n    url = \"http://python.sinaapp.com\",\n    packages=find_packages(),\n    package_data={'sae': ['channel.js']},\n    scripts = scripts,\n    zip_safe = False,\n)\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = _build\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n\n.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest\n\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\nclean:\n\t-rm -rf $(BUILDDIR)/*\n\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/SAEPythonReferences.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/SAEPythonReferences.qhc\"\n\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/SAEPythonReferences\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SAEPythonReferences\"\n\t@echo \"# devhelp\"\n\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\tmake -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n"
  },
  {
    "path": "docs/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# SAE Python References documentation build configuration file, created by\n# sphinx-quickstart on Fri Sep 23 11:25:19 2011.\n#\n# This file is execfile()d with the current directory set to its containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\nimport sys, os\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\nsys.path.insert(0, os.path.abspath('../../Lib/'))\nsys.path.insert(0, os.path.abspath('exts'))\n\n# -- General configuration -----------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be extensions\n# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.\nextensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 'sphinx.ext.ifconfig']\nextensions += ['chinese_search',]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix of source filenames.\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u\"SAE Python Developer's Guide\"\ncopyright = u'2011, SAE Python Team'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = '1.0'\n# The full version, including alpha/beta/rc tags.\nrelease = '1.0(beta)'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#language = None\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\nexclude_patterns = ['_build']\n\n# The reST default role (used for this markup: `text`) to use for all documents.\n#default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n#add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n\n# -- Options for HTML output ---------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\nhtml_theme = 'nature'\n\nhtml_search_language = 'zh_CN'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#html_theme_options = {}\n\n# Add any paths that contain custom themes here, relative to this directory.\nhtml_theme_path = ['theme']\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\n#html_title = None\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#html_short_title = None\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = None\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n#html_favicon = None\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['static']\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\n#html_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n#html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_domain_indices = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#html_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = None\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'SAEPythonReferencesdoc'\n\n\n# -- Options for LaTeX output --------------------------------------------------\n\n# The paper size ('letter' or 'a4').\n#latex_paper_size = 'letter'\n\n# The font size ('10pt', '11pt' or '12pt').\n#latex_font_size = '10pt'\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title, author, documentclass [howto/manual]).\nlatex_documents = [\n  ('index', 'SAEPythonReferences.tex', u'SAE Python References Documentation',\n   u'SAE Python Team', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#latex_use_parts = False\n\n# If true, show page references after internal links.\n#latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n#latex_show_urls = False\n\n# Additional stuff for the LaTeX preamble.\n#latex_preamble = ''\n\n# Documents to append as an appendix to all manuals.\n#latex_appendices = []\n\n# If false, no module index is generated.\n#latex_domain_indices = True\n\n\n# -- Options for manual page output --------------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    ('index', 'saepythonreferences', u'SAE Python References Documentation',\n     [u'SAE Python Team'], 1)\n]\n\n\n# -- Options for Epub output ---------------------------------------------------\n\n# Bibliographic Dublin Core info.\nepub_title = u'SAE Python References'\nepub_author = u'SAE Python Team'\nepub_publisher = u'SAE Python Team'\nepub_copyright = u'2011, SAE Python Team'\n\n# The language of the text. It defaults to the language option\n# or en if the language is not set.\n#epub_language = ''\n\n# The scheme of the identifier. Typical schemes are ISBN or URL.\n#epub_scheme = ''\n\n# The unique identifier of the text. This can be a ISBN number\n# or the project homepage.\n#epub_identifier = ''\n\n# A unique identification for the text.\n#epub_uid = ''\n\n# HTML files that should be inserted before the pages created by sphinx.\n# The format is a list of tuples containing the path and title.\n#epub_pre_files = []\n\n# HTML files shat should be inserted after the pages created by sphinx.\n# The format is a list of tuples containing the path and title.\n#epub_post_files = []\n\n# A list of files that should not be packed into the epub file.\n#epub_exclude_files = []\n\n# The depth of the table of contents in toc.ncx.\n#epub_tocdepth = 3\n\n# Allow duplicate toc entries.\n#epub_tocdup = True\n"
  },
  {
    "path": "docs/exts/chinese_search.py",
    "content": "from sphinx.search import SearchLanguage\nimport jieba\n\nclass SearchChinese(SearchLanguage):\n    lang = 'zh'\n\n    def init(self, options):\n        pass\n\n    def split(self, input):\n        return jieba.cut_for_search(input.encode(\"utf8\")) \n\n    def word_filter(self, stemmed_word):\n        return True\n\ndef setup(app): \n    import sphinx.search as search\n    search.languages[\"zh_CN\"] = SearchChinese\n"
  },
  {
    "path": "docs/faq.rst",
    "content": "FAQ\n===============\n\nBUG反馈以及问题求助\n-------------------------\n\n关于SAE Python相关服务的问题可以在以下地方反馈和提问。\n\n* `SAE Python邮件列表`_ （推荐）\n\n  订阅邮件列表\n  发送邮件至： sae-python+subscribe@googlegroups.com\n  \n  退订邮件列表\n  发送邮件至： sae-python+unsubscribe@googlegroups.com\n  \n  发表主题\n  发送邮件至： sae-python@googlegroups.com\n  \n  订阅和退订时发送空邮件即可。如果想修改订阅方式，可以登录Google论坛后在设置中进行更改。\n\n\n* `SAE Python豆瓣小组（一些旧帖子的存档） <http://www.douban.com/group/pythoncitadel/>`_\n\n关于Python编程的其它问题，推荐到 `CPyUG邮件列表`_ 寻求帮助。\n\n.. _SAE Python邮件列表: http://groups.google.com/group/sae-python\n.. _CPyUG邮件列表: http://groups.google.com/group/python-cn?hl=zh-CN\n\n如何调试\n------------\n\n复杂程序建议您本地调试成功后，再上传运行。\n\nSAE Python 版本为 2.7.3，本地调试时注意不要使用高于此版本的python。\n\n如果你使用内置的第三方库，本地调试时最好使用同样的版本。\n\n对于50x页面，如果异常被web框架捕获，则需要打开web框架的调试开关，查看详细的异常信息。如果异常被python server捕获，\npython server会直接在浏览器中打印出异常。\n\n.. note:: 在header已经发出的情况下，异常在浏览器中可能显示不出来，请查看日志。\n\n\n没有我要使用的包怎么办\n------------------------\n\n对于纯python的package，参考 :ref:`howto-use-saecloud-install`\n\n对于含有c extension的package，目前SAE还无法直接支持，如果需要这些package，可以申请预装。\n\n`预装申请`_\n\n.. _预装申请: https://github.com/sinacloud/sae-python-dev-guide/issues/new\n\n.. note::\n\n   很多package对package里的c extension都会提供一个python的fallback版本，这类package在sae上也可以\n   直接使用，只是速度上相对于使用c extension会稍慢一点。\n\n\n如何使用新浪微博API\n----------------------\n\n+   使用 `weibopy`_\n\n    该模块已经内置，可以直接使用。 完整示例请参考： `examples/weibo`_  。\n\n+   使用 `sinaweibopy`_ (推荐)\n\n    新浪微博API OAuth 2 Python客户端\n\n.. _weibopy: http://code.google.com/p/sinatpy/\n.. _examples/weibo: https://github.com/sinacloud/sae-python-dev-guide/tree/master/examples/weibo/1\n.. _sinaweibopy: http://open.weibo.com/wiki/SDK#Python_SDK\n\n\n如何在Cron中使用微博API\n------------------------\n\n因为现在weibo api需要提供调用者的ip（合法的公网ip），sae默认提供的是http请求的client的ip，\n但是对于cron和taskqueue，由于是sae的内部请求，无法获取公网ip。所以需要用户手工设置一个。\n设置方法如下： ::\n\n    import os\n    os.environ['REMOTE_ADDR'] = 调用者公网ip\n\n请务必将这段代码放在请求处理代码执行的必经路径上。比如在Flask中：::\n\n    @app.before_request\n    def before_request():\n        import os\n        os.environ['REMOTE_ADDR'] = 调用者公网ip\n\n关于svn的问题 \n--------------------------- \n\n遇到奇怪的SVN错误，可以： \n\n+ 重新在本地新建目录，检出干净的svn \n+ 或者先保存代码，然后删除该版本，重新导入 \n\n你也许需要新建一个版本，默认版本无法删除。 \n\n\nMySQL gone away问题\n----------------------\n\nMySQL连接超时时间为30s，不是mysql默认的8小时，所以你需要在代码中检查是否超时，是否需要重连。\n\n对于使用sqlalchemy的用户，需要在请求处理结束时调用 `db.session.close()` ，关闭当前session，\n将mysql连接还给连接池，并且将连接池的连接recyle时间设的小一点（推荐为10s）。\n\nMySQL InterfaceError: (-1, 'error totally whack(xxx)')\n---------------------------------------------------------\n\n这个错误表示mysql返回的错误码是sae自定义的错误码，其中totally whack后面括号中的数字是具体的错误码。\n\n你可以在 `自定义错误码`_ 查询到相关错误码具体代表的信息。\n\n.. _自定义错误码:  http://sae.sina.com.cn/?m=devcenter&catId=203#anchor_ba2cfd662e37730bc2bf2fb0fea566c7\n\n\n如何区分本地开发环境和线上环境\n-------------------------------------\n::\n\n    if 'SERVER_SOFTWARE' in os.environ: \n        # SAE \n    else: \n        # Local \n\n如何使用virtualenv管理依赖关系\n-------------------------------\n\n当你的应用依赖很多第三方包时，可以使用virtualenv来管理并导出这些依赖包，\n流程如下：\n\n首先，创建一个全新的Python虚拟环境目录ENV，启动虚拟环境。 ::\n\n    $ virtualenv --no-site-packages ENV\n    $ source ENV/bin/activate\n    (ENV)$\n\n可以看到命令行提示符的前面多了一个(ENV)的前缀，现在我们已经在一个全新的虚拟环境中了。\n\n使用pip安装应用所依赖的包并导出依赖关系到requirements.txt。 ::\n\n    (ENV)$ pip install Flask Flask-Cache Flask-SQLAlchemy \n    (ENV)$ pip freeze > requirements.txt\n\n编辑requirements.txt文件，删除一些sae内置的模块，eg. flask, jinja2, wtforms。\n\n使用dev_server/bundle_local.py工具，\n将所有requirements.txt中列出的包导出到本地目录virtualenv.bundle目录中。\n如果文件比较多的话，推荐压缩后再上传。 ::\n\n    (ENV)$ bundle_local.py -r requirements.txt\n    (ENV)$ cd virtualenv.bundle/\n    (ENV)$ zip -r ../virtualenv.bundle.zip .\n\n将virutalenv.bundle目录或者virtualenv.bundle.zip拷贝到应用的目录下。\n\n修改index.wsgi文件，在导入其它模块之前，将virtualenv.bundle目录或者\nvirtualenv.bundle.zip添加到module的搜索路径中，示例代码如下： ::\n\n    import os\n    import sys\n\n    app_root = os.path.dirname(__file__)\n\n    # 两者取其一\n    sys.path.insert(0, os.path.join(app_root, 'virtualenv.bundle'))\n    sys.path.insert(0, os.path.join(app_root, 'virtualenv.bundle.zip'))\n\n到此，所有的依赖包已经导出并加入到应用的目录里了。\n\n更多virtualenv的使用可以参考其官方文档。 http://pypi.python.org/pypi/virtualenv\n\n.. note::\n\n   1. 请删除requirements.txt中的wsgiref==0.1.2这个依赖关系，否则可能导致\n      bundle_local.py导出依赖包失败。\n\n   2. 有些包是not-zip-safe的，可能不工作，有待验证。 含有c扩展的package\n      不能工作。\n\nMatplotlib使用常见问题\n-----------------------\n\nSAE环境不支持matplotlib的interative模式，所以无法使用 `pyplot.show()` 直接来显示图像，只能使用\n`pyplot.savefig()` 将图像保存到一个输出流中（比如一个cStringIO.StringIO的实例中）。\n\n如果想要在matplotlib中显示中文，可以使用以下任一方法。\n\n    方法一： ::\n\n        import os.path\n        from matplotlib.font_manager import FontProperties\n        zh_font = FontProperties(fname=os.path.abspath('wqy-microhei.ttf'))\n        import matplotlib.pyplot as plt\n        plt.title(u'中文', fontproperties=zh_font)\n\n    方法二： ::\n\n        import os\n        # 设置自定义字体文件所在目录路径，多条路径之间使用分号（:）隔开\n        os.environ['TTFPATH'] = os.getcwd()\n        import matplotlib\n        # 设置默认字体名\n        matplotlib.rcParams['font.family'] = 'WenQuanYi Micro Hei'\n        import matplotlib.pyplot as plt\n        plt.title(u'中文')\n\n其中方法一适用于ttf和ttc字体，方法二适用于只适用于ttf字体\n\n如果有 `matplotlibrc` 配置文件，请将该文件与index.wsgi放在同一个目录下（默认的当前路径）。\n\n\n.. _wsgi_middleware:\n\n设置基于主机的访问控制\n----------------------------\n\npython runtime目前无法通过config.yaml来配置基于主机的访问控制，用户如果需要这个设置，可以通过wsgi middleware来完成。 ::\n\n    def filter_middleware(app):\n        def _(environ, start_response):\n            remote_addr = environ.get('REMOTE_ADDR')\n\n            # 判断remote_addr是否在允许访问范围内\n            # ...\n\n            if ok:\n                return app(environ, start_response)\n\n            start_response('401 Unauthorized', [])\n            return [\"<b>401 Unauthorized</b>\",]\n        return _\n\n    # 给application加上访问控制的中间件\n    application = filter_middleware(application)\n"
  },
  {
    "path": "docs/index.rst",
    "content": ".. SAE Python References documentation master file, created by\n   sphinx-quickstart on Fri Sep 23 11:25:19 2011.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\nSAE Python 开发者手册\n=================================================\n\n本文档，示例代码以及本地测试server下载：  ::\n\n    git clone https://github.com/sinacloud/sae-python-dev-guide.git\n\nContents:\n    \n.. toctree::\n    :maxdepth: 2\n    \n    quickstart\n    runtime\n    service\n    tools\n    faq \n\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n\n"
  },
  {
    "path": "docs/quickstart.rst",
    "content": "Quick Start 快速指引\n==============================\n\nHello, world!\n-----------------\n\n首先我们以一个简单的hello world应用介绍一下一个Python应用在SAE上的创建和部署过程。\n\n创建应用\n~~~~~~~~~~~~~\n\n登录SAE，进入 `我的首页`_ ，点击 `创建新应用` ，创建一个新的应用helloworld。\n开发语言选择python。\n\n.. _我的首页: http://sae.sina.com.cn/?m=myapp&a=create\n\n编辑应用代码\n~~~~~~~~~~~~~\n\nSAE采用svn来作为代码部署工具，使用svn客户端检出应用helloworld的代码。 ::\n\n    jaime@westeros:~$ svn co https://svn.sinaapp.com/helloworld\n\n每个应用可以创建多个版本，这些版本可以在线上同时运行，每个版本以数字标示，\n其代码对应于svn目录下以其版本号命名的目录。\n\n进入helloworld目录，创建一个目录1作为默认版本，切换到目录1。 ::\n\n    jaime@westeros:~$ cd helloworld\n    jaime@westeros:~/helloworld$ mkdir 1\n    jaime@westeros:~/helloworld$ cd 1\n\n创建应用配置文件config.yaml，内容如下： ::\n\n    name: helloworld\n    version: 1\n\n创建index.wsgi，内容如下：\n\n.. literalinclude:: ../examples/helloworld/1/index.wsgi\n\nSAE上的python应用的入口为 `index.wsgi:application` ，也就是 `index.wsgi` 这个文件中名为\n`application` 的callable object。在helloworld应用中，该application为一个wsgi callable object。\n\n部署应用\n~~~~~~~~~~~\n\n提交刚刚编辑的代码，就可以完成应用在SAE上的部署。\n\n::\n\n    jaime@westeros:~/helloworld$ svn add 1/\n    jaime@westeros:~/helloworld$ svn ci -m \"initialize project\"\n\n\n在浏览器中输入 `http://helloworld.sinaapp.com` ，就可以访问刚提交的应用了。\n\n.. note:: \n\n   svn的仓库地址为：https://svn.sinaapp.com/<your-application-name>，\n   用户名和密码为sae的安全邮箱和安全密码。\n\n使用web开发框架\n-----------------\n\nDjango\n~~~~~~~~~~\n\n目前SAE上预置了两个版本的django，1.2.7和1.4，默认的版本为1.2.7，在本示例中我们使用1.4版本。\n\n创建一个新的python应用，检出svn代码到本地目录并切换到应用目录。\n\n创建一个django project：mysite。 ::\n\n    jaime@westeros:~/pythondemo$ django-admin.py startproject mysite\n    jaime@westeros:~/pythondemo$ ls mysite\n    manage.py  mysite/\n\n重命名该project的根目录名为1，作为该应用的默认版本代码目录。 ::\n\n    jaime@westeros:~/pythondemo$ mv mysite 1\n\n在默认版本目录下创建应用配置文件 `config.yaml` ，在其中添加如下内容： ::\n\n    libraries:\n    - name: \"django\"\n      version: \"1.4\"\n\n创建文件index.wsgi，内容如下 ::\n    \n    import sae \n    from mysite import wsgi\n\n    application = sae.create_wsgi_app(wsgi.application)\n\n最终目录结构如下 ::\n\n    jaime@westeros:~/pythondemo$ ls 1\n    config.yaml index.wsgi manage.py mysite/ \n    jaime@westeros:~/pythondemo/1$ ls 1/mysite\n    __init__.py settings.py  urls.py  views.py\n\n部署代码，访问 `http://<your-application-name>.sinaapp.com` ，就可看到Django的欢迎页面了。\n\n`完整示例`_ （ `django tutorial`_ 中的poll、choice程序）\n\n.. _django tutorial: https://docs.djangoproject.com/en/1.4/intro/tutorial01/\n.. _完整示例: https://github.com/sinacloud/sae-python-dev-guide/tree/master/examples/django/1.4\n\n`django-1.2.7示例`_\n\n.. _django-1.2.7示例: https://github.com/sinacloud/sae-python-dev-guide/tree/master/examples/django/1.2.7\n\n\n处理用户上传文件\n``````````````````\n\n在setttings.py中添加以下配置。 ::\n\n    # 修改上传时文件在内存中可以存放的最大size为10m\n    FILE_UPLOAD_MAX_MEMORY_SIZE = 10485760\n\n    # sae的本地文件系统是只读的，修改django的file storage backend为Storage\n    DEFAULT_FILE_STORAGE = 'sae.ext.django.storage.backend.Storage'\n    # 使用media这个bucket\n    STORAGE_BUCKET_NAME = 'media'\n    # ref: https://docs.djangoproject.com/en/dev/topics/files/\n\n发送邮件\n``````````\n\n在settings.py中添加以下配置，即可使用sae的mail服务来处理django的邮件发送了。 ::\n\n    ADMINS = (\n        ('administrator', 'administrator@gmail.com'),\n    )\n\n    # ref: https://docs.djangoproject.com/en/dev/ref/settings/#email\n    EMAIL_BACKEND = 'sae.ext.django.mail.backend.EmailBackend'\n    EMAIL_HOST = 'smtp.example.com'\n    EMAIL_PORT = 587\n    EMAIL_HOST_USER = 'sender@gmail.com'\n    EMAIL_HOST_PASSWORD = 'password'\n    EMAIL_USE_TLS = True\n    SERVER_EMAIL = DEFAULT_FROM_EMAIL = EMAIL_HOST_USER\n\n数据库的主从读写\n``````````````````\n\n参见Django官方文档 `Multiple databases`_\n\n.. _Multiple databases: https://docs.djangoproject.com/en/1.2/topics/db/multi-db/#multiple-databases\n\n如何syncdb到线上数据库\n````````````````````````\n\n如下配置数据库，即可执行 `python manage.py syncdb` 直接syncdb到线上数据库。 ::\n\n    # 线上数据库的配置\n    MYSQL_HOST = 'w.rdc.sae.sina.com.cn'\n    MYSQL_PORT = '3307'\n    MYSQL_USER = 'ACCESSKEY'\n    MYSQL_PASS = 'SECRETKEY'\n    MYSQL_DB   = 'app_APP_NAME'\n\n    from sae._restful_mysql import monkey\n    monkey.patch()\n\n    DATABASES = {\n        'default': {\n            'ENGINE':   'django.db.backends.mysql',\n            'NAME':     MYSQL_DB,\n            'USER':     MYSQL_USER,\n            'PASSWORD': MYSQL_PASS,\n            'HOST':     MYSQL_HOST,\n            'PORT':     MYSQL_PORT,\n        }\n    }\n\n.. warning:: 本feature还在开发中，目前还很buggy。\n\n如何serve admin app的静态文件\n``````````````````````````````\n\n方法一：\n\n修改 `settings.py` 中的 `STATIC_ROOT` 为 `static/` 。\n\n运行 `python manage.py collectstatic` 将静态文件收集到应用的 `static` 子目录下。\n\n修改 `config.yaml` ，添加对static文件夹下的静态文件的handlers。 ::\n\n    handlers:\n    - url: /static\n      static_dir: path/to/mysite/static\n\n方法二：\n\n在开发调试（settings.py中debug=True）过程中，可以将 `staticfiles_urlpatterns`_ 加到你的URLConf，让django来处理admin app的静态文件： ::\n\n    urls.py\n    --------\n    from django.contrib import admin\n    admin.autodiscover()\n\n    urlpatterns = patterns('',\n        ...\n\n        # Uncomment the next line to enable the admin:\n        url(r'^admin/', include(admin.site.urls)),\n    )\n\n    from django.contrib.staticfiles.urls import staticfiles_urlpatterns\n    urlpatterns += staticfiles_urlpatterns()\n\n由于sae默认static为静态文件目录，需要修改config.yaml，添加任意一条规则覆盖默认行为。 ::\n\n    config.yaml\n    -----------\n    ...\n\n    handlers:\n    - url: /foo\n      static_dir: foo\n\nref:\n\nhttps://docs.djangoproject.com/en/1.4/ref/contrib/staticfiles/\nhttps://docs.djangoproject.com/en/1.4/howto/deployment/wsgi/modwsgi/#serving-the-admin-files\n\n.. _staticfiles_urlpatterns: https://docs.djangoproject.com/en/dev/howto/static-files/#staticfiles-development\n\nFlask\n~~~~~~~~~~\n\nindex.wsgi\n\n.. literalinclude:: ../examples/flask/index.wsgi\n\nmyapp.py \n\n.. literalinclude:: ../examples/flask/myapp.py\n\n\nBottle\n~~~~~~~~~~\n\nindex.wsgi\n\n.. literalinclude:: ../examples/bottle/index.wsgi\n\nweb.py\n~~~~~~~~\n\nindex.wsgi\n\n.. literalinclude:: ../examples/webpy/index.wsgi\n\nTornado\n~~~~~~~~~~~\n\n**WSGI模式**\n\nindex.wsgi\n\n.. literalinclude:: ../examples/tornado/wsgi/index.wsgi\n\n**使用Torando Worker**\n\nconfig.yaml\n\n.. literalinclude:: ../examples/tornado/async/config.yaml\n\nindex.wsgi\n\n.. literalinclude:: ../examples/tornado/async/index.wsgi\n\n.. note::\n\n   1. tornado worker还处在bleeding edge，use at your own risk。\n   2. 在应用出现异常的情况下，SAE可能会返回502错误，请在日志中心中查看详细的错误信息。\n   3. 目前仅测试过预安装的tornado-2.1.1，其它版本的tornado可能无法使用。\n   4. 对于tornado worker，如果需要使用非预装的tornado，请务必将tornado模块放在应用根目录下（index.wsgi所在的目录）。\n\n\n.. tip:: \n   \n   以上所有的示例代码的完整版本都可以在我们的github repo中获得。\n   https://github.com/sinacloud/sae-python-dev-guide/tree/master/examples/\n\n"
  },
  {
    "path": "docs/runtime.rst",
    "content": "SAE Python环境\n=======================\n\nSAE应用运行于沙箱环境之中，SAE会根据负载在后端的多个节点中选择一个来处理HTTP请求。\nSAE Python支持标准WSGI应用。\n\n请求处理\n-------------\n\nSAE使用请求的域名来决定处理的应用。比如 `http://your_app_name.sinaapp.com` 的请求会被路由给名为\n`your_app_name` 的应用来处理。\n\n每个应用可以同时运行多个版本，版本以数字为标示，默认版本为1。如果需要访问非默认版本，\n需要在域名的前面加上版本号作为子域名： `http://version.your_app_name.sinaapp.com` 。\n  \n默认URL符合以下规则的请求会作为静态文件处理：\n\n* /static/\\*\n* /favicon.ico\n\n其他所有请求，都被路由到/index.wsgi:application，即应用根目录index.wsgi文件,\n名为application的callable，不可修改。\n\napplication 使用下列方式创建\n\n.. module:: sae\n.. function:: create_wsgi_app(app)\n\n   将标准wsgi应用封装为适宜在SAE上运行的应用\n\n::\n\n    import sae\n\n    def app(environ, start_response):\n        # Your app\n        ...\n\n    application = sae.create_wsgi_app(app)\n\n所有请求的最大执行时间为 `300s` ，超过该时间的请求会被系统强制结束，可能会导致返回502。\n\n对于每个应用的请求，系统会为其维护一个队列，如果出现请求在一定的时间里得不到处理，\n服务器会自动给该应用增加instance。如果某一个instance在一段时间里没有任何请求，会被系统回收。\n\nPython环境\n-------------------\n\nPython runtime使用的是Python 2.7.3。\n\n* 仅支持运行纯Python的应用，不能动态加载C扩展，即.so，.dll等格式的模块不能使用\n* 进程，线程操作受限\n* 本地文件系统只读。应用可以读取本应用目录，Python标准库下的内容，如需读写临时文件建议使用StringIO或者cStringIO来替代。\n\nPython默认的模块搜索路径为：当前目录 > 系统目录。添加模块搜索目录的方法为： ::\n\n    import sys\n    sys.path.insert(0, your_custom_module_path)\n\n注意：Python当前目录下的子目录只有包含__init__.py才会被Python认为是一个package，\n才可以直接import。\n\n用户可以上传和使用 `.pyc` 文件，注意 `.pyc` 文件必须是python2.7.3生成的，否则无效。\n\nSAE设置了一些自定义的环境变量，这些环境变量可以通过os.environ这个dict获取。 \n\n+ APP_NAME：应用名。\n+ APP_VERSION: 当前应用使用的版本号。\n+ SERVER_SOFTWARE: 当前server的版本（目前为direwolf/0.1）。\n  可以使用这个环境变量来区分本地开发环境还是在线环境，本地开发环境未设置这个值。\n\n日志系统\n---------\n打印到stdout和stderr的内容会记录到应用的日志中心中，\n所以直接使用print语句或者logging模块来记录应用的日志就可以了。\n\n日志内容在 `应用»日志中心» HTTP` 中查看，类别为debug。\n\n.. note:: logging默认设置的level是WARNING，也就是level >= WARNING的消息才会被输出。\n\n应用缓存\n----------\n\nSAE Python会对应用导入的模块（包括index.wsgi）进行缓存，从而缩短请求响应时间，\n对于缓存了的应用，请求处理只是取出index.wsgi中application这个callable并调用。\n\n\n应用程序配置\n-------------\n\n应用程序的配置文件为应用目录下的config.yaml文件。\n\n* 设置使用的worker类型 ::\n\n    worker: tornado | gevent | wsgi\n\n  如果没有设置则默认使用wsgi worker。\n\n  .. warning:: gevent和tornado worker还处在bleeding edge, use at your own risk。\n\n* 使用预装模块\n\n  部分预装的第三方模块不在python的默认模块搜索路径中，需要在config.yaml中配置后才可以使用。 ::\n\n    libraries:\n    - name: django\n      version: \"1.4\"\n\n    - name: numpy\n      version: \"1.6.1\"\n\n  name为第三方模块的名称，version为需要使用的版本，这两个字段为必填字段。 :ref:`pre-installed-package-list`\n\n* 静态文件处理 \n\n  ``静态文件夹`` ::\n\n    handlers:\n    - url: /static/\n      static_path: static\n  \n  url为URL的前缀，static_path为静态文件所在的目录（相对于应用目录）。\n\n  当请求的url为目录时，服务器会首先尝试static_path下的index.html，当index.html存在时，\n  返回index.html的内容，否则返回404。\n\n  ``静态文件``  ::\n\n    handlers:\n    - url: /robots.txt\n      static_path: robots.txt\n\n    - url: /favicon.ico\n      static_path: favicon.ico\n\n* gzip压缩 ::\n\n    handlers:\n    - url: /static/\n      gzip: on\n\n    - url: /a-big-file.txt\n      gzip: on\n\n  url为URL的前缀。\n\n  注意，当客户端支持gzip（HTTP请求的Accept-Encoding中包含gzip）时，服务端才会开启压缩。\n\n.. note::\n\n   1. 部分第三方库已经包含在默认搜索路径中，可以不在config.yaml中指定直接使用。\n\n   2. 如果config.yaml中没有设置静态文件相关的handlers，系统会默认将/static为前缀\n      的URL转发到应用目录下的static目录。\n\n   3. 以上两条规则仅为兼容性考虑保留，不推荐使用，请在config.yaml明确配置。\n\n   4. 对于php runtime支持但是python runtime不支持的appconfig，请考虑使用wsgi middleware来完成，参见 :ref:`wsgi_middleware` \n\n.. _pre-installed-package-list:\n\n预装模块列表\n---------------------\n\n    =============================== =================== ====================\n    名称                            支持的版本          默认版本\n    =============================== =================== ====================\n    django                          1.2.7, 1.4, 1.5     1.2.7\n    flask                           0.7.2               0.7.2\n    flask-sqlalchemy                0.15                0.15\n    werkzeug                        0.7.1               0.7.1\n    jinja2                          2.6                 2.6\n    tornado                         2.1.1, 2.4.1, 3.1.1 2.1.1\n    bottle                          0.9.6               0.9.6\n    sqlalchemy                      0.7.10              0.7.10\n    webpy                           0.36                0.36\n    PIL                             1.1.7               1.1.7\n    MySQLdb                         1.2.3               1.2.3\n    numpy                           1.6.1               无\n    lxml                            2.3.4               无\n    PyYAML                          3.10                3.10\n    misaka                          1.0.2               无\n    matplotlib                      1.1.1               无\n    PyCrypto                        2.6                 无\n    py-bcrypt                       0.2                 无\n    greenlet                        0.4.0               0.4.0\n    gevent                          1.0rc2              1.0rc2\n    markupsafe                      0.15                无\n    bitarray                        0.8.0               无\n    =============================== =================== ====================\n\n.. note:: 如果模块不存在默认版本或者需要使用非默认版本请在应用配置文件config.yaml中指定。\n\n\n"
  },
  {
    "path": "docs/service.rst",
    "content": "可用服务列表\n=========================\n\n注意：MySQL, TaskQueue, Memcache, KVDB 服务需开启才能使用，请在前端管理界面 `服务管理` 中开启并初始化。\n\n访问互联网\n-------------\n\n直接使用urllib, urllib2或者httplib模块访问网络资源即可。\n\n默认使用sae提供的fetchurl服务来抓取网页，对于fetchurl不支持的功能（HTTP代理等），可以禁用掉fetchurl服务，\n使用socket服务来处理请求。 ::\n\n    import os\n    os.environ['disable_fetchurl'] = \"1\"\n\nMySQL\n------------\n\n连接信息\n~~~~~~~~~~~\n\n获取mysql的连接信息。\n\n.. module:: sae.const\n   :synopsis: MYSQL连接信息\n\n::\n\n    import sae.const\n\n    sae.const.MYSQL_DB      # 数据库名\n    sae.const.MYSQL_USER    # 用户名\n    sae.const.MYSQL_PASS    # 密码\n    sae.const.MYSQL_HOST    # 主库域名（可读写）\n    sae.const.MYSQL_PORT    # 端口，类型为<type 'str'>，请根据框架要求自行转换为int\n    sae.const.MYSQL_HOST_S  # 从库域名（只读）\n\n下面就可以跟平常一样使用MySQL服务了，SAE Python内置了MySQLdb模块，对于MySQLdb的使用，可以参考其 `官方文档`_ 。\n\n.. _官方文档: http://mysql-python.sourceforge.net/MySQLdb.html\n\n注意： MySQL 连接超时时间为30s 。\n\n数据库管理\n~~~~~~~~~~~~~~\n\n你可以使用 `sae-python-dev` 包里的 `cloudsql.py` 这个mysql命令行客户端来管理数据库。详细见 :ref:`cloudsql.py`\n\nSAE支持使用PHPMyAdmin来管理Mysql，点击管理面板 `服务管理 > MySQL > 管理MySQL` 即可进入。\n\n开发时可使用本地mysql数据库，在发布时再将其导入到SAE线上数据库中。\n\n.. note::\n\n   导入sql文件时，请先删除所有的LOCK, UNLOCK语句。\n\n   在管理界面创建数据表，默认字符集为utf8，也可设为其他编码。\n   如果你是在本地开发环境建立的数据表，请确保使用utf8。在管理界面导入本地数据库时，\n   也可完成字符集的转换。\n\nTaskQueue, Cron\n---------------\n\n什么是任务\n~~~~~~~~~~~~~\n出于安全性考虑，SAE不支持执行一段任意的代码程序。SAE的cron，和unix的cron意义不同，没有相关联的程序。\n\nSAE的任务，实际上对应于一个URL地址。SAE worker节点每请求一次该URL，就算执行一次任务。\n真正的任务处理代码，是app中处理该URL的handler。\n\n任务执行有两种方式: Taskqueue 动态执行任务, Cron 定时执行任务\n\n任务的执行情况可以在日志中心>TaskQueue栏中查询。\n\nTaskqueue\n~~~~~~~~~~~~~~\n\n.. module:: sae.taskqueue\n\n.. py:function:: add_task(queue_name, url, payload=None, **kws)\n\n   快速添加任务    \n\n   queue_name: 任务队列的名称\n\n   url: 任务的url，如： /tasks/task_name\n\n   payload: 可选，如果payload存在且不为None，则该任务为一POST任务，payload会作为请求\n   的POST的数据。\n\n   delay/prior: 可选，使用方法参见Task。\n\n\n.. py:class:: Task(url, payload=None, **kwargs)\n\n   Task类\n     \n   url: 任务的url，如： /tasks/task_name\n\n   payload: 可选, 如果payload存在且不为None，则该任务为一POST任务，payload会作为请求\n   的POST的数据。\n\n   delay: 可选，设置任务延迟执行的时间，单位为秒，最大可以为600秒。\n\n   prior: 可选，如果设置为True，则任务会被添加到任务队列的头部。\n \n.. py:class:: TaskQueue(name, auth_token=None)\n\n   TaskQueue类\n\n   name: 任务队列的名称。\n\n   auth_token: 可选, 一个包含两个元素的元组 (access_key, secretkey_key)。\n    \n   .. py:method:: add(task)\n\n      添加一个任务\n          \n      task: 添加的任务，可以为单个Task任务，也可以是一个Task列表。\n\n   .. py:method:: size()\n\n      获取当前队列中还有多少未执行的任务。\n\n\nExample:\n\n1. 添加一个任务。   ::\n    \n    from sae.taskqueue import Task, TaskQueue\n\n    queue = TaskQueue('queue_name')\n    queue.add(Task(\"/tasks/foo\"))\n\n2. 添加一个POST任务。   ::\n\n    queue.add(Task(\"/tasks/bar\", \"data\"))\n\n3. 批量添加任务。   ::\n\n    tasks = [Task(\"/tasks/update\", user) for user in users]\n    queue.add(tasks)\n\n4. 快速添加任务。   ::\n\n    from sae.taskqueue import add_task\n    add_task('queue_name', '/tasks/push', 'msg')\n\n..  note:: \n\n    任务的url现在已经改为相对的url，目前兼容绝对url，但是不推荐使用。 \n    任务默认使用GET方式请求，如果Task带有payload参数且不为None则使用POST方式请求。\n\nCron\n~~~~~~~~~~~~~~~~\n\nCron的配置文件为 `config.yaml` ，Cron的执行状态可在应用的管理界面 `服务管理>Cron` 中查看。\n\n+   添加Cron:\n\n    编辑config.yaml文件中，增加cron段，例如：   ::\n\n        name: crontest\n        version: 1\n        cron:\n        - description: cron_test\n          url: /cron/make\n          schedule: \"*/5 * * * *\"\n\n    上面的示例添加了一个cron任务，\n    该任务每5分钟执行 `http://crontest.sinaapp.com/cron/make` 一次。\n\n+   删除cron:\n\n    删除config.yaml中对应的cron描述段即可就行。\n\n+   语法字段含义\n\n    - url\n\n      cron任务的url。例如 `/relative/url/to/cron` 。\n     \n    - schedule\n\n      任务描述，也就是何时执行这个cron，支持unix crontab语法。例如：  ::\n\n               # 每天00：05分执行\n               \"5 0 * * *\"\n               # 每月1号的14：15分执行\n               \"15 14 1 * *\"\n               # 每个工作日的晚上10点执行\n               \"0 22 * * 1-5\"\n               # 每分钟执行一次\n               \"*/1 * * * *\"\n\n      具体的语法规则可以参考man手册， `man 5 crontab`_ 。\n        \n    - description\n\n      可选。任务的说明，默认为空。\n     \n    - timezone\n\n      可选。默认为Beijing，目前支持：Beijing, NewYork, London, Sydney, Moscow, Berlin\n     \n    - login\n\n      可选。http basic auth设置，格式： `用户名@密码`\n     \n    - times\n\n      可选。设置cron最大执行的次数，默认没有次数限制。\n\n.. _man 5 crontab: http://man.he.net/man5/crontab\n\n..  note::\n\n    Cron使用GET方式请求URL。\n\nCron 完整示例\n~~~~~~~~~~~~~~~~~~~\n每五分钟请求一次 /backend/cron/update URL\n\nFlask URL 处理程序::\n\n    import datetime\n    import pylibmc as memcache\n\n    from appstack import app\n\n    mc = memcache.Client(['localhost'])\n\n    @app.route('/backend/cron/update', methods=['GET', 'POST'])\n    def update():\n        update_time = mc.get('update_time')\n        mc.set(\"update_time\", str(datetime.datetime.now()))\n\n        return update_time\n\nconfig.yaml::\n\n    name: appstack\n    version: 4\n\n    cron:\n    - url: /backend/cron/update\n      schedule: \"*/5 * * * *\"\n\nMail\n-----------\n\nMail是SAE为开发者提供的邮件发送服务，用来异步发送标准SMTP邮件。Mail的日志可以在 `日志中心» Mail` 中查看。\n\n..  module:: sae.mail\n\n..  py:class:: EmailMessage(**kwargs)\n    :module: sae.mail\n\n    EmailMessage类\n\n    参数同下面的initialize\n\n    ..  py:method:: initialize(\\**kwargs)\n\n        初始化邮件的内容。\n\n        to: 收件人，收件人邮件地址或者收件人邮件地址的列表。\n\n        subject: 邮件的标题。\n\n        body/html: 邮件正文。如果内容为纯文本，使用body，如果是html则使用html。\n\n        smtp: smtp服务器的信息。是一个包含5个元素的tuple。\n        (smtp主机，smtp端口， 邮件地址或用户名，密码，是否启用TLS）。\n\n        from_addr: 可选。发件人，邮件的from字段，默认使用smtp的配置信息。\n\n        attachments: 可选。邮件的附件，必须为一个list，list里每个元素为一个\n        tuple，tuple的第一个元素为文件名，第二个元素为文件的内容。\n\n    ..  py:method:: send\n\n        提交邮件发送请求至后端服务器。\n\n    ..  py:method:: __setattr__(attr, value)\n\n        attr: 属性名。 value: 属性的值。\n\n..  py:function:: send_mail(to, subject, body, smtp, **kwargs)\n    :module: sae.mail\n\n    快速发送邮件。\n\n    字段的意义同EmailMessage.initialize()。\n    \n\nExamle:\n\n1.  快速发送一份邮件 ::\n\n        from sae.mail import send_mail\n\n        send_mail(\"katherine@vampire.com\", \"invite\", \"to tonight's party\",\n                  (\"smtp.vampire.com\", 25, \"damon@vampire.com\", \"password\", False))\n\n2.  发送邮件给多个收件人 ::\n\n        to = [\"katherine@vampire.com\", 'rebecca@vampire.com', 'elena@vampire.com']\n        send_mail(to, \"invite\", \"to tonight's party\",\n                  (\"smtp.vampire.com\", 25, \"damon@vampire.com\", \"password\", False))\n    \n\n2.  发送一封html格式的邮件 ::\n\n        from sae.mail import EmailMessage\n\n        m = EmailMessage()\n        m.to = 'damon@vampire.com'\n        m.subject = 'Re: inivte'\n        m.html = '<b>my pleause!</b>'\n        m.smtp = ('smtp.vampire.com', 25, 'katherine@vampire.com', 'password', False)\n        m.send()\n\n3.  使用Gmail SMTP  ::\n\n        import sae.mail\n\n        sae.mail.send_mail(to, subject, body,\n                ('smtp.gmail.com', 587, from, passwd, True))\n\nMemcache\n-----------\n请在前端管理界面启用Memcache服务。\n\n..  module:: pylibmc\n    :synopsis: memcache模块\n\nSAE Python使用 http://sendapatch.se/projects/pylibmc/ 作为mc客户端。\n不同之处在于，创建Client时不用指定servers参数（如果指定了会被忽略）。\n\n示例代码::\n\n    import pylibmc as memcache\n\n    mc = memcache.Client()\n \n    mc.set(\"foo\", \"bar\")\n    value = mc.get(\"foo\")\n \n    if not mc.get('key'):\n        mc.set(\"key\", \"1\")\n    mc.incr(\"key\")\n\npylibmc接口和 `python-memcached`_ 基本兼容，可以直接替换使用。 `python-memcache文档 <_static/memcache.html>`_ 。\n\n对于现有使用python-memcache的代码，可以直接在index.wsgi中（任何import memcache语句执行之前）加入如下代码，即可不修改代码使用pylibmc了。 ::\n\n    import pylibmc\n    import sys\n    sys.modules['memcache'] = pylibmc\n\n.. _python-memcached: http://www.tummy.com/Community/software/python-memcached/\n\nStorage\n----------\n\nStorage是SAE为开发者提供的分布式文件存储服务，用来存放用户的持久化存储的文件。\n\n用户需要先在storage的管理面板中创建保存数据的容器（bucket）。bucket中保存实际的数据（object）。\n\n.. note:: 这里的bucket就是管理面板中的domain。\n\nTutorial\n~~~~~~~~~~~\n\n在SAE runtime中操作storage  ::\n\n    >>> # 创建一个bucket的instance\n    >>> from sae.storage import Bucket\n    >>> bucket = Bucket('t')\n\n    >>> # 创建该bucket\n    >>> bucket.put()\n\n    >>> # 修改该bucket的acl和缓存过期时间。\n    >>> bucket.post(acl='.r:.sinaapp.com,.r:sae.sina.com.cn', metadata={'expires': '1d'})\n\n    >>> # 获取该bucket的属性信息\n    >>> attrs = bucket.stat()\n    >>> print attrs\n    {'acl': '.r:.sinaapp.com,.r:sae.sina.com.cn',\n     'bytes': '0',\n     'metadata': {'expires': '1d'},\n     'objects': '0'}\n    >>> attrs.acl\n    '.r:.sinaapp.com,.r:sae.sina.com.cn'\n\n    >>> # 存取一个字符串到bucket中\n    >>> bucket.put_object('1.txt', 'hello, world')\n\n    >>> # 获取object的public url\n    >>> bucket.generate_url('1.txt')\n    'http://pylabs-t.stor.sinaapp.com/1.txt'\n\n    >>> # 存取一个文件到bucket中\n    >>> bucket.put_object('2.txt', open(__file__, 'rb'))\n    \n    >>> # 列出该bucket中的所有objects\n    >>> [i for i in bucket.list()]\n    [{u'bytes': 12,\n      u'content_type': u'text/plain',\n      u'hash': u'e4d7f1b4ed2e42d15898f4b27b019da4',\n      u'last_modified': u'2013-05-22T05:09:32.259140',\n      u'name': u'1.txt'},\n     {u'bytes': 14412,\n      u'content_type': u'rb',\n      u'hash': u'99079422784f6cbfc4114d9b261001e1',\n      u'last_modified': u'2013-05-22T05:12:13.337400',\n      u'name': u'2.txt'}]\n\n    >>> # 获取object的所有属性\n    >>> bucket.stat_object('1.txt')\n    {'bytes': '12',\n     'content_type': 'text/plain',\n     'hash': 'e4d7f1b4ed2e42d15898f4b27b019da4',\n     'last_modified': '2013-05-22T05:09:32.259140',\n     'metadata': {},\n     'timestamp': '1369199372.25914'}\n\n    >>> # 取object的内容\n    >>> bucket.get_object_contents('1.txt')\n    'hello, world'\n\n    >>> # 取文件的内容，返回generator\n    >>> chunks = bucket.get_object_contents('2.txt', chunk_size=10)\n    <generator object _body at 0x95ec20c>\n    >>> chunks.next()      # 显示第一个chunk\n    '\\n# Copyrig'\n\n    >>> # 删除objects\n    >>> bucket.delete_object('1.txt')\n    >>> bucket.delete_object('2.txt')\n\n    >>> # 删除bucket\n    >>> bucket.delete()\n\nstorage中不支持创建实际的目录，但是用户可以通过在object name中加入 `/` 来模拟目录结构。例如： ::\n\n    >>> bucket.put_object('a/1.txt', '')\n    >>> bucket.put_object('a/b/2.txt', '')\n\n    >>> # 列出根目录下的所有objects。\n    >>> [i for i in bucket.list(path='')]\n    [{u'bytes': None,\n      u'content_type': None,    # content_type为None表示这是一个子目录\n      u'hash': None,\n      u'last_modified': None,\n      u'name': 'a/'}]\n\n    >>> # 列出a目录下的objects\n    >>> [i for i in bucket.list(path='a/')]\n    [{u'bytes': 0,\n      u'content_type': u'text/plain',\n      u'hash': u'd41d8cd98f00b204e9800998ecf8427e',\n      u'last_modified': u'2013-05-23T03:01:59.051030',\n      u'name': u'a/1.txt'},\n     {u'bytes', None,\n      u'content_type': None,    # content_type为None表示这是一个子目录\n      u'hash': None,\n      u'last_modified': None,\n      u'name': u'a/b/'}]\n\n.. note:: 在list目录（除了根目录）时路径的末尾要带上 / 。\n\n如果是在外部，SAE storage支持swift协议，用户也可以直接使用 `python-swiftclient`_ 客户端来操作storage。 ::\n\n    $ pip install python-swiftclient\n    $ export ST_AUTH='https://auth.sinas3.com/v1.0'\n    $ export ST_USER='<your-accesskey>'\n    $ export ST_KEY='<your-secretkey>'\n    $ swift -h\n\n.. _python-swiftclient: https://pypi.python.org/pypi/python-swiftclient\n\nAPI Reference\n~~~~~~~~~~~~~~~~\n\n..  module:: sae.storage\n\n..  py:class:: Error()\n\n    Storage异常类。\n\n..  module:: sae.storage\n\n..  py:class:: Connection(accesskey=ACCESS_KEY, secretkey=SECRET_KEY, account=APP_NAME, retries=3)\n\n    Connection类\n\n    accesskey: 可选。storage归属的应用的accesskey，默认为当前应用。\n\n    secretkey: 可选。storage归属的应用的secretkey，默认为当前应用。\n\n    account: 可选。storage归属的应用的应用名，默认为当前应用。\n\n    retries: 请求失败时重试的次数。\n\n    .. py:method:: get_bucket(bucket)\n\n       获取一个bucket类的instance。\n\n       bucket: bucket的名称。\n\n..  module:: sae.storage\n\n..  py:class:: Bucket(bucket, conn=None)\n\n    Bucket类，封装了大部分的storage操作。\n\n    bucket: bucket的名称。\n\n    conn: 可选。一个sae.storage.Connection的实例。\n\n    .. py:method:: put(acl=None, metadata=None)\n\n       创建一个bucket。\n\n       acl: bucket的 :ref:`about-acl` (Access Control List)。\n\n       metadata: 需要保存的元数据，metadata应该是一个dict，例如 `{'color': 'blue'}` 。\n\n    .. py:method:: post(acl=None, metadata=None)\n\n       修改bucket的acl和metadata。其中metadata的修改为增量修改。\n\n    .. py:method:: list(prefix=None, delimiter=None, path=None, limit=10000, marker=None)\n\n       列出bucket中的object。\n\n       prefix: object名称的前缀。\n\n       delimiter: 分割字符。折叠包含该分割字符的条目。\n\n       path: 返回路径path下的全部objects。等价于prefix为path，delimiter为/。\n\n       limit: 最大返回的objects条数。\n\n       marker: 返回object名为marker后面的结果（不包含marker）。\n\n       返回符合条件的objects的属性的一个generator。\n\n    .. py:method:: stat()\n\n       返回当前bucket的属性信息。\n\n    .. py:method:: delete()\n\n       删除bucket。被删除的bucket必须已经清空（没有任何object）。\n\n    .. py:method:: put_object(obj, contents, content_type=None, content_encoding=None, metadata=None)\n\n       创建或更新一个object。\n\n       obj: object的名称。\n\n       contents: object的内容，可以是字符串或file-like object。\n\n       content_type: object的mime类型。\n\n       content_encoding: object的encoding。\n\n       metadata: object的元数据。\n\n    .. py:method:: post_object(obj, content_type=None, content_encoding=None, metadata=None)\n\n       更新object的一些属性。\n\n       注意：object的metadata的更新是全量的，和container的增量修改不一样。\n\n    .. py:method:: get_object(obj, chunk_size=None)\n\n       获取object的内容和属性信息。\n\n       obj: object的名称。\n\n       chunk_size: 不返回object的全部内容，而是返回一个文件内容的generator，每次iterate返回chunk_size大小的数据。\n\n       返回一个tuple (obj的属性信息, object的内容)。\n\n    .. py:method:: get_object_contents(obj, chunk_size=None)\n\n       获取object的内容。\n\n       参数同get_object，只返回object的内容。\n\n    .. py:method:: stat_object(obj)\n\n       获取object的属性信息。\n\n    .. py:method:: delete_object(obj)\n\n       删除object。\n\n    .. py:method:: generate_url(obj)\n\n       返回object的public url。\n\n\n.. _about-acl:\n\nACL\n~~~~~~~~~~\n\nACL的格式是： `[item[,item...]]` 。 每一个item指定了一个referer的访问权限，item的格式为： `.r:[-]value` 。 \n\n+ value可以为 `*` （任何referrer host都可以访问）\n+ 如果value的开头包含 `.` 或者 `*` ，则其为一个域名通配。\n  比如： `.example.com` 或者 `*.example.com` 。表示以.example.com结尾的referer host可以访问。\n+ 在value的前面加上 `-` 则表示禁止referer host为这一域名的访问。\n+ acl为空时表示不允许任何referer host的访问，也就等于该bucket为私有bucket。\n\n例：  ::\n\n    .r:*\n    .r:-.thief.com\n    .r:.example.com,.r:-thief.example.com\n\n缓存过期\n~~~~~~~~~~~~~~\n\n如果用户没有设置bucket或者object的缓存过期时间，storage默认的时间是2小时。\n\n用户可以通过以下metadata设置object的缓存过期时间: ::\n\n    expires       该metadata可以加在bucket或者object上，用来设置object的缓存过期。\n                  其value的格式为：[modified] time_delta\n\n    expires-type  该metadata可以加在bucket上。对于该bucket中的object，按照其mime_type来设置对应的缓存过期时间。\n                  其value的格式为：mime_type [modified] time_delta\n                  如果有多条规则，规则和规则之间使用逗号（，）隔开。\n\nmime_type为object的mime类型，例如： text/html, text/plain, image/gif\n\ntime_delta是一个表示时间的字符串，例如： `1y3M`, `48d`, `5s`, `-1` ::\n\n    s   seconds\n    m   minutes\n    h   hours\n    d   days\n    w   weeks\n    M   months, 30 days\n    y   years, 365 days\n\nmodified关键字用于指定缓存过期的时间相对于object的最后修改时间。默认expire时间是相对于访问时间。\n\n如果time_delta为负， Cache-Control header会被设置为no-cache. \n\n正的time_delta会设置Cache-Control为max-age = #，其中 # 是缓存过期的时间（单位为秒）。\n\n模拟storage为文件系统\n~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n    from sae.ext.storage import monkey\n    monkey.patch_all()\n\n以上代码会将storage ‘挂载’到 `/s/` 目录下，每个bucket为这个目录下的一个子目录。用户可以使用 `/s/<bucket-name>/<object-name>` 这样形式的路径通过文件系统接口来访问storage的中的object。\n\n目前支持（patch）的文件系统接口函数为： `open, os.listdir, os.mkdir, os.path.exists, os.path.isdir, os.open, os.fdopen, os.close, os.chmod, os.stat, os.unlink, os.rmdir`\n\nKVDB\n----------\n\nkvdb服务使用前需要在 `管理面板`_ 中启用，不再使用时可以在面板中禁用，禁用会删除所有数据。\n\n.. _管理面板: http://sae.sina.com.cn/?m=kv\n\n..  module:: sae.kvdb\n\n..  py:class:: Error\n    :module: sae.kvdb\n\n    通用错误\n\n..  py:class:: RouterError\n    :module: sae.kvdb\n\n    路由meta信息错误\n\n..  py:class:: StatusError\n    :module: sae.kvdb\n\n    kvdb状态不为OK\n\n..  py:class:: KVClient(debug=0)\n    :module: sae.kvdb\n\n    debug 是否输出详细调试、错误信息到日志，默认关闭\n\n    .. py:method:: set(key, val, min_compress_len=0)\n\n       设置key的值为val\n\n       min_compress_len 启用zlib.compress压缩val的最小长度，如果val的长度大于此值\n       则启用压缩，0表示不压缩。\n\n    .. py:method:: add(key, val, min_compress_len=0)\n\n       同set，但只在key不存在时起作用\n\n    .. py:method:: replace(key, val, min_compress_len=0)\n\n       同set，但只在key存在时起作用\n\n    .. py:method:: delete(key)\n\n       删除key。\n\n    .. py:method:: get(key)\n\n       从kvdb中获取一个key的值，存在返回key的值，不存在则返回None\n\n    .. py:method:: get_multi(keys, key_prefix='')\n\n       从kvdb中一次获取多个key的值。返回一个key/value的dict。\n\n       keys: key的列表，类型必须为list。\n\n       key_prefix: 所有key的前缀。请求时会在所有的key前面加上该前缀，返回值里所有的key都会去掉该前缀。\n\n    .. py:method:: get_by_prefix(prefix, limit=100, marker=None)\n\n       从kvdb中查找指定前缀的 key/value pair。返回一个generator，yield的item为一个(key, value)的tuple。\n\n       prefix: 需要查找的key的前缀。\n\n       limit: 最多返回的item个数，默认为100。\n\n       marker: 指定从哪一个key开始继续查找，只返回该key后面的结果（该key不含在内）。\n\n    .. py:method:: getkeys_by_prefix(prefix, limit=100, marker=None)\n\n       从kvdb中查找指定前缀的key。返回符合条件的key的generator。\n\n       prefix: 需要查找的key的前缀。\n\n       limit: 最多返回的key的个数，默认为100。\n\n       marker: 指定从哪一个key开始继续查找，只返回该key后面的结果（该key不含在内）。\n\n    .. py:method:: get_info()\n\n       获取本应用kvdb统计数据，返回一个字典::\n\n            {\n                'outbytes': 126, \n                'total_size': 3, \n                'inbytes': 180, \n                'set_count': 60,\n                'delete_count': 21, \n                'total_count': 1, \n                'get_count': 42\n            }\n\n    .. py:method:: disconnect_all()\n        \n       关闭kvdb连接\n\n示例代码: ::\n\n    import sae.kvdb\n\n    kv = sae.kvdb.KVClient()\n\n    k = 'foo'\n    kv.set(k, 2)\n    kv.delete(k)\n\n    kv.add(k, 3)\n    kv.get(k)\n\n    kv.replace(k, 4)\n    kv.get(k)\n\n    print kv.get_info()\n\n服务限制:\n\n+ 存储空间：100G\n+ 最大记录条数：1,000,000,000\n+ key的最大长度：200 Bytes\n+ value的最大长度：4M\n+ get_multi获取的最大KEY个数：32\n\nSOCKET\n-----------------\n\n直接使用socket模块即可。目前仅支持AF_INET, SOCK_STREAM连接，暂时不支持异步socket。bind/listen方法无法使用。\n\nChannel\n--------------\n\n概述\n~~~~~~~\n\nchannel是sae提供的实时消息推送服务。通过在浏览器和SAE服务端之间建立长连接，使得应用可以方便的向javascript客户端实时的推送消息。\n\n下图为channel服务的大致使用流程:\n\n.. image:: static/channel-overview.png\n   :height: 400px\n\nchannel服务的使用主要包含两个部分：JS客户端，服务端处理程序。\n\n对于JS客户端，其需要完成：\n\n+ 使用应用服务端创建的channel url连接上channel的服务器。\n+ 设置下行消息的处理函数。\n+ 需要发送消息时，调用send发送消息给channel服务器（或者直接使用XMLHttpRequest直接发送给应用服务端也可）。\n\n对于应用的服务端：\n\n+ 调用create_channel为每个客户端创建一个channel，并将channel的url返回给客户端。\n+ 处理channel server发过来的客户端上行消息（如果有的话）。\n+ 有消息要发送时，调用send_message来向客户端推送消息。\n\n当JS客户端和channel服务端连接上/断开或者当JS客户端有发送消息给channel服务器时，channel服务器会使用http回调的方式通知应用。\n\n=========================== ============================================================\n回调地址                    事件说明\n=========================== ============================================================\n/_sae/channel/connected     客户端连接上channel服务器\n/_sae/channel/disconnected  客户端和channel服务器断开\n/_sae/channel/message       客户端有上行消息，POST内容的message字段为JS客户端发送的内容\n=========================== ============================================================\n\n所有的http回调都使用POST方法。所有http回调的POST内容中的from字段为客户端对应channel的名称。其余字段（如果有）见具体回调说明。\n\nServer端API：\n\n.. module:: sae.channel\n\n.. py:function:: create_channel(name, duration=None)\n\n   创建一个channel。\n\n   name: channel的名称。\n\n   duration: channel的有效时间，单位为秒，默认为1小时。\n\n.. py:function:: send_message(name, message)\n\n   向名为name的channel里发送一条消息\n\n   name: channel的名称\n\n   message: 需要发送的消息内容\n\n.. warning:: 最大可以发送4k的消息，最好不要直接发送二进制的数据。\n\n\nJavascript客户端API：\n\n在html页面中使用以下代码引用channel服务的js库。 ::\n\n    <script type=\"text/javascript\" src=\"/_sae/channel/api.js\"></script>\n\n.. js:class:: sae.Channel(url)\n\n   :param string url: 服务端create_channel()返回的url地址\n\n   .. js:attribute:: onopen \n\n      设置客户端连接上服务端时的回调函数。\n\n   .. js:attribute:: onmessage\n\n      设置客户端收到消息时的回调函数。该函数接受一个参数：一个messagae对象，其中的data字段为服务端send_message接口发送的消息内容。\n\n   .. js:attribute:: onerror\n\n      设置客户端和服务端连接出现错误时的回调函数。\n\n   .. js:attribute:: onclose\n\n      设置客户端关闭和服务端的连接时的回调函数。\n\n服务使用示例\n~~~~~~~~~~~~~~\n\n下面我们使用 `TicTacToe(井字棋)`_ 游戏来示范channel服务的使用方法：\n\n.. _TicTacToe(井字棋): http://zh.wikipedia.org/wiki/%E4%BA%95%E5%AD%97%E6%A3%8B\n\n完整代码：https://github.com/sinacloud/sae-channel-examples/tree/master/python\n\n**channel的创建和连接**\n\n首先，当用户A打开TicTacToe游戏的主页时，TicTacToe服务端的程序会：\n\n+ 调用 `create_channel` 创建为用户A创建一个channel，并将该channel的url嵌入到返回给用户的html页面代码中。\n+ 生成一个加入游戏的连接，用户通过将此连接发送给其它用户B，其它用户B可以通过此连接加入用户A创建的游戏。\n\n每个页面对应的channel的name应该是独一无二的，比如可以使用用户id的字符串作为channel的name。\n\n游戏的主页的html代码模板大致如下所示，其中 `{{ url }}` 和 `{{ game_link }}` 分别为上面生成的channel url和游戏加入连接。 ::\n\n    <head>\n    ...\n    <script src=\"/_sae/channel/api.js\"></script>\n    </head>\n    <body>\n      <script>\n        channel = new sae.Channel('{{ url }}');\n        socket.onopen = onOpened;\n        socket.onmessage = onMessage;\n        socket.onerror = onError;\n        socket.onclose = onClose;\n      </script>\n\n      ...\n\n      <div id='other-player' style='display:none'>\n        Waiting for another player to join.<br>\n        Send them this link to play:<br>\n        <div id='game-link'><a href='{{ game_link }}'>{{ game_link }}</a></div>\n      </div>\n\n    </body>\n\n游戏的js客户端使用 `sae.Channel` 来创建一条channel连接，并且设置channel的onopen/onmessage/onerror/onclose的callback函数。\n\n**使用channel来推送游戏状态信息**\n\n当用户B点击用户A发过来的连接打开了游戏页面时，游戏的javascript客户端通过 `sendMessage` 函数通知服务端。 ::\n\n    onOpened = function() {\n      connected = true;\n      sendMessage('opened');\n      updateBoard();\n    };\n\n    sendMessage = function(path, opt_param) {\n      path += '?g=' + state.game_key;\n      if (opt_param) {\n        path += '&' + opt_param;\n      }\n      var xhr = new XMLHttpRequest();\n      xhr.open('POST', path, true);\n      xhr.send();\n    };\n\n服务端更新当前游戏的状态，并且通过channel的 `send_message` 将游戏的新的状态发送给用户A和用户B的channel客户端。客户端接受到消息后更新游戏页面。此后用户A和用户B交替走棋，客户端通过 `sendMessage` 将用户的走法发送给服务端。 ::\n\n    moveInSquare = function(id) {\n      if (isMyMove() && state.board[id] == ' ') {\n        sendMessage('/move', 'i=' + id);\n      }\n    }\n\n服务收到消息后更新游戏的状态，再通过 `send_message` 将更新后的状态发送给用户A和B，如此往复直到游戏结束为止。 ::\n\n    class MovePage(tornado.web.RequestHandler):\n\n      def post(self):\n        game_key = self.get_argument('g')\n        game = Game.get_by_key_name(game_key)\n        user = self.get_secure_cookie('u')\n        if game and user:\n          id = int(self.get_argument('i'))\n          GameUpdater(game).make_move(id, user)\n\n    class GameUpdater():\n      game = None\n\n      def __init__(self, game):\n        self.game = game\n\n      def get_game_message(self):\n        gameUpdate = {\n          'board': self.game.board,\n          'userX': self.game.userX,\n          'userO': '' if not self.game.userO else self.game.userO,\n          'moveX': self.game.moveX,\n          'winner': self.game.winner,\n          'winningBoard': self.game.winning_board\n        }\n        return json.dumps(gameUpdate)\n\n      def send_update(self):\n        message = self.get_game_message()\n        channel.send_message(self.game.userX + self.game.key_name, message)\n        if self.game.userO:\n          channel.send_message(self.game.userO + self.game.key_name, message)\n\n      def check_win(self):\n        if self.game.moveX:\n          # O just moved, check for O wins\n          wins = Wins().o_wins\n          potential_winner = self.game.userO\n        else:\n          # X just moved, check for X wins\n          wins = Wins().x_wins\n          potential_winner = self.game.userX\n          \n        for win in wins:\n          if win.match(self.game.board):\n            self.game.winner = potential_winner\n            self.game.winning_board = win.pattern\n            return\n\n      def make_move(self, position, user):\n        if position >= 0 and user == self.game.userX or user == self.game.userO:\n          if self.game.moveX == (user == self.game.userX):\n            boardList = list(self.game.board)\n            if (boardList[position] == ' '):\n              boardList[position] = 'X' if self.game.moveX else 'O'\n              self.game.board = \"\".join(boardList)\n              self.game.moveX = not self.game.moveX\n              self.check_win()\n              self.game.put()\n              self.send_update()\n              return\n\nGameUpdater类检查move的请求是否合法，如果合法则更新游戏的状态并且通知游戏双方新的游戏状态。\n\n.. note::\n\n   1. 每个html页面最多可以建立1个channel连接。\n   2. 每个创建的channel只允许一个channel客户端连接。\n\n中文分词\n-------------------\n\n**分词服务请求**\n\nSAE分词服务请求采用以下形式的HTTP网址： ::\n\n    http://segment.sae.sina.com.cn/urlclient.php?parameters\n\nparameters为请求参数，多个参数之间使用&分割，以下列出了这些参数和其可能的值。\n\n* encoding: 请求分词的文本的编码，可以为: GB18030、UTF-8、UCS-2。\n* word_tag: 可选。是否返回词性数据。0表示不返回，1表示返回。\n\n请求分词的文本以post的形式提交。\n\n* context: 请求分词的文本。目前限制文本大小最大为10KB。\n\n**分词服务响应**\n\n分词服务的响应数据为json格式，格式如下： ::\n\n    [\n        {\"word\":\"采莲\",\"word_tag\":\"171\",\"index\":\"1\"},\n        {\"word\":\"赋\",\"word_tag\":\"170\",\"index\":\"2\"}\n    ]\n\n响应数据为一个list，list中每个元素为一个dict，每个dict中包含以下数据：\n\n* index: 序列号，按在请求文本中的位置依次递增。\n* word: 单词\n* word_tag: 单词的词性，仅当输入parameters里word_tag为1时包含该项。\n\n词性代码： ::\n\n    0   POSTAG_ID_UNKNOW 未知\n    10  POSTAG_ID_A      形容词\n    20  POSTAG_ID_B      区别词\n    30  POSTAG_ID_C      连词\n    31  POSTAG_ID_C_N    体词连接\n    32  POSTAG_ID_C_Z    分句连接\n    40  POSTAG_ID_D      副词\n    41  POSTAG_ID_D_B    副词(\"不\")\n    42  POSTAG_ID_D_M    副词(\"没\")\n    50  POSTAG_ID_E      叹词\n    60  POSTAG_ID_F      方位词\n    61  POSTAG_ID_F_S    方位短语(处所词+方位词)\n    62  POSTAG_ID_F_N    方位短语(名词+方位词“地上”)\n    63  POSTAG_ID_F_V    方位短语(动词+方位词“取前”)\n    64  POSTAG_ID_F_Z    方位短语(动词+方位词“取前”)\n    70  POSTAG_ID_H      前接成分\n    71  POSTAG_ID_H_M    数词前缀(“数”---数十)\n    72  POSTAG_ID_H_T    时间词前缀(“公元”“明永乐”)\n    73  POSTAG_ID_H_NR   姓氏\n    74  POSTAG_ID_H_N    姓氏\n    80  POSTAG_ID_K      后接成分\n    81  POSTAG_ID_K_M    数词后缀(“来”--,十来个)\n    82  POSTAG_ID_K_T    时间词后缀(“初”“末”“时”)\n    83  POSTAG_ID_K_N    名词后缀(“们”)\n    84  POSTAG_ID_K_S    处所词后缀(“苑”“里”)\n    85  POSTAG_ID_K_Z    状态词后缀(“然”)\n    86  POSTAG_ID_K_NT   状态词后缀(“然”)\n    87  POSTAG_ID_K_NS   状态词后缀(“然”)\n    90  POSTAG_ID_M      数词\n    95  POSTAG_ID_N      名词\n    96  POSTAG_ID_N_RZ   人名(“毛泽东”)\n    97  POSTAG_ID_N_T    机构团体(“团”的声母为t，名词代码n和t并在一起。“公司”)\n    98  POSTAG_ID_N_TA   ....\n    99  POSTAG_ID_N_TZ   机构团体名(\"北大\")\n    100 POSTAG_ID_N_Z    其他专名(“专”的声母的第1个字母为z，名词代码n和z并在一起。)\n    101 POSTAG_ID_NS     名处词\n    102 POSTAG_ID_NS_Z   地名(名处词专指：“中国”)\n    103 POSTAG_ID_N_M    n-m,数词开头的名词(三个学生)\n    104 POSTAG_ID_N_RB   n-rb,以区别词/代词开头的名词(该学校，该生)\n    107 POSTAG_ID_O      拟声词\n    108 POSTAG_ID_P      介词\n    110 POSTAG_ID_Q      量词\n    111 POSTAG_ID_Q_V    动量词(“趟”“遍”)\n    112 POSTAG_ID_Q_T    时间量词(“年”“月”“期”)\n    113 POSTAG_ID_Q_H    货币量词(“元”“美元”“英镑”)\n    120 POSTAG_ID_R      代词\n    121 POSTAG_ID_R_D    副词性代词(“怎么”)\n    122 POSTAG_ID_R_M    数词性代词(“多少”)\n    123 POSTAG_ID_R_N    名词性代词(“什么”“谁”)\n    124 POSTAG_ID_R_S    处所词性代词(“哪儿”)\n    125 POSTAG_ID_R_T    时间词性代词(“何时”)\n    126 POSTAG_ID_R_Z    谓词性代词(“怎么样”)\n    127 POSTAG_ID_R_B    区别词性代词(“某”“每”)\n    130 POSTAG_ID_S      处所词(取英语space的第1个字母。“东部”)\n    131 POSTAG_ID_S_Z    处所词(取英语space的第1个字母。“东部”)\n    132 POSTAG_ID_T      时间词(取英语time的第1个字母)\n    133 POSTAG_ID_T_Z    时间专指(“唐代”“西周”)\n    140 POSTAG_ID_U      助词\n    141 POSTAG_ID_U_N    定语助词(“的”)\n    142 POSTAG_ID_U_D    状语助词(“地”)\n    143 POSTAG_ID_U_C    补语助词(“得”)\n    144 POSTAG_ID_U_Z    谓词后助词(“了、着、过”)\n    145 POSTAG_ID_U_S    体词后助词(“等、等等”)\n    146 POSTAG_ID_U_SO   助词(“所”)\n    150 POSTAG_ID_W      标点符号\n    151 POSTAG_ID_W_D    顿号(“、”)\n    152 POSTAG_ID_W_SP   句号(“。”)\n    153 POSTAG_ID_W_S    分句尾标点(“，”“；”)\n    154 POSTAG_ID_W_L    搭配型标点左部\n    155 POSTAG_ID_W_R    搭配型标点右部(“》”“]”“）”)\n    156 POSTAG_ID_W_H    中缀型符号\n    160 POSTAG_ID_Y      语气词(取汉字“语”的声母。“吗”“吧”“啦”)\n    170 POSTAG_ID_V      及物动词(取英语动词verb的第一个字母。)\n    171 POSTAG_ID_V_O    不及物谓词(谓宾结构“剃头”)\n    172 POSTAG_ID_V_E    动补结构动词(“取出”“放到”)\n    173 POSTAG_ID_V_SH   动词“是”\n    174 POSTAG_ID_V_YO   动词“有”\n    175 POSTAG_ID_V_Q    趋向动词(“来”“去”“进来”)\n    176 POSTAG_ID_V_A    助动词(“应该”“能够”)\n    180 POSTAG_ID_Z      状态词(不及物动词,v-o、sp之外的不及物动词)\n    190 POSTAG_ID_X      语素字\n    191 POSTAG_ID_X_N    名词语素(“琥”)\n    192 POSTAG_ID_X_V    动词语素(“酹”)\n    193 POSTAG_ID_X_S    处所词语素(“中”“日”“美”)\n    194 POSTAG_ID_X_T    时间词语素(“唐”“宋”“元”)\n    195 POSTAG_ID_X_Z    状态词语素(“伟”“芳”)\n    196 POSTAG_ID_X_B    状态词语素(“伟”“芳”)\n    200 POSTAG_ID_SP     不及物谓词(主谓结构“腰酸”“头疼”)\n    201 POSTAG_ID_MQ     数量短语(“叁个”)\n    202 POSTAG_ID_RQ     代量短语(“这个”)\n    210 POSTAG_ID_AD     副形词(直接作状语的形容词)\n    211 POSTAG_ID_AN     名形词(具有名词功能的形容词)\n    212 POSTAG_ID_VD     副动词(直接作状语的动词)\n    213 POSTAG_ID_VN     名动词(指具有名词功能的动词)\n    230 POSTAG_ID_SPACE  空格\n\n例： ::\n\n    chinese_text = \"\"\"\n    这里填上需要分词的文本\n    \"\"\"\n\n    _SEGMENT_BASE_URL = 'http://segment.sae.sina.com.cn/urlclient.php'\n\n    payload = urllib.urlencode([('context', chinese_text),])\n    args = urllib.urlencode([('word_tag', 1), ('encoding', 'UTF-8'),])\n    url = _SEGMENT_BASE_URL + '?' + args\n    result = urllib2.urlopen(url, payload).read()\n\n短信\n------------------\n\n**短信服务请求**\n\nSAE短信服务请求采用以下形式的HTTP网址： ::\n\n    http://inno.smsinter.sina.com.cn/sae_sms_service/sendsms.php\n\n参数采用POST的方法提交，以下列出了这些参数和其可能的值。\n\n* mobile: 对方的手机号码。\n* msg: 短信发送的内容。\n* encoding: 可选。短信发送内容的编码格式，可以为：GB2312、UTF-8，默认为GB2312。\n\n**短信服务响应**\n\n短信服务的响应为json格式，格式如下： ::\n\n    {\n        \"sms\": {\n            \"encoding\": \"GB2312\", \n            \"mobile\": \"18911978203\", \n            \"msg\": \"hello.....\"\n        }, \n        \"status\": \"\\\\u77ed\\\\u4fe1\\\\u63d0\\\\u4ea4\\\\u6210\\\\u529f\"\n    }\n\n响应数据为一个dict，dict中包含以下数据:\n\n* sms: 短信服务请求的参数。\n* status：短信发送的状态。 \n\n.. warning:: 短信接口默认msg参数的编码格式为GB2312，如果短信的内容包含中文，请务必正确的编码msg或者选择正确的encoding参数。\n"
  },
  {
    "path": "docs/static/memcache.html",
    "content": "\n<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<html><head><title>Python: module memcache</title>\n</head><body bgcolor=\"#f0f0f8\">\n\n<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"heading\">\n<tr bgcolor=\"#7799ee\">\n<td valign=bottom>&nbsp;<br>\n<font color=\"#ffffff\" face=\"helvetica, arial\">&nbsp;<br><big><big><strong>memcache</strong></big></big> (version 1.48)</font></td\n><td align=right valign=bottom\n><font color=\"#ffffff\" face=\"helvetica, arial\"><a href=\".\">index</a><br><a href=\"file:/tmp/python-memcached-1.48/memcache.py\">/tmp/python-memcached-1.48/memcache.py</a></font></td></tr></table>\n    <p><tt>client&nbsp;module&nbsp;for&nbsp;memcached&nbsp;(memory&nbsp;cache&nbsp;daemon)<br>\n&nbsp;<br>\nOverview<br>\n========<br>\n&nbsp;<br>\nSee&nbsp;U{the&nbsp;MemCached&nbsp;homepage&lt;<a href=\"http://www.danga.com/memcached\">http://www.danga.com/memcached</a>&gt;}&nbsp;for&nbsp;more&nbsp;about&nbsp;memcached.<br>\n&nbsp;<br>\nUsage&nbsp;summary<br>\n=============<br>\n&nbsp;<br>\nThis&nbsp;should&nbsp;give&nbsp;you&nbsp;a&nbsp;feel&nbsp;for&nbsp;how&nbsp;this&nbsp;module&nbsp;operates::<br>\n&nbsp;<br>\n&nbsp;&nbsp;&nbsp;&nbsp;import&nbsp;memcache<br>\n&nbsp;&nbsp;&nbsp;&nbsp;mc&nbsp;=&nbsp;memcache.<a href=\"#Client\">Client</a>(['127.0.0.1:11211'],&nbsp;debug=0)<br>\n&nbsp;<br>\n&nbsp;&nbsp;&nbsp;&nbsp;mc.set(\"some_key\",&nbsp;\"Some&nbsp;value\")<br>\n&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;=&nbsp;mc.get(\"some_key\")<br>\n&nbsp;<br>\n&nbsp;&nbsp;&nbsp;&nbsp;mc.set(\"another_key\",&nbsp;3)<br>\n&nbsp;&nbsp;&nbsp;&nbsp;mc.delete(\"another_key\")<br>\n&nbsp;<br>\n&nbsp;&nbsp;&nbsp;&nbsp;mc.set(\"key\",&nbsp;\"1\")&nbsp;&nbsp;&nbsp;#&nbsp;note&nbsp;that&nbsp;the&nbsp;key&nbsp;used&nbsp;for&nbsp;incr/decr&nbsp;must&nbsp;be&nbsp;a&nbsp;string.<br>\n&nbsp;&nbsp;&nbsp;&nbsp;mc.incr(\"key\")<br>\n&nbsp;&nbsp;&nbsp;&nbsp;mc.decr(\"key\")<br>\n&nbsp;<br>\nThe&nbsp;standard&nbsp;way&nbsp;to&nbsp;use&nbsp;memcache&nbsp;with&nbsp;a&nbsp;database&nbsp;is&nbsp;like&nbsp;this::<br>\n&nbsp;<br>\n&nbsp;&nbsp;&nbsp;&nbsp;key&nbsp;=&nbsp;derive_key(obj)<br>\n&nbsp;&nbsp;&nbsp;&nbsp;obj&nbsp;=&nbsp;mc.get(key)<br>\n&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;not&nbsp;obj:<br>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;obj&nbsp;=&nbsp;backend_api.get(...)<br>\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mc.set(key,&nbsp;obj)<br>\n&nbsp;<br>\n&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;we&nbsp;now&nbsp;have&nbsp;obj,&nbsp;and&nbsp;future&nbsp;passes&nbsp;through&nbsp;this&nbsp;code<br>\n&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;will&nbsp;use&nbsp;the&nbsp;object&nbsp;from&nbsp;the&nbsp;cache.<br>\n&nbsp;<br>\nDetailed&nbsp;Documentation<br>\n======================<br>\n&nbsp;<br>\nMore&nbsp;detailed&nbsp;documentation&nbsp;is&nbsp;available&nbsp;in&nbsp;the&nbsp;L{<a href=\"#Client\">Client</a>}&nbsp;class.</tt></p>\n<p>\n<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"section\">\n<tr bgcolor=\"#aa55cc\">\n<td colspan=3 valign=bottom>&nbsp;<br>\n<font color=\"#ffffff\" face=\"helvetica, arial\"><big><strong>Modules</strong></big></font></td></tr>\n    \n<tr><td bgcolor=\"#aa55cc\"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>\n<td width=\"100%\"><table width=\"100%\" summary=\"list\"><tr><td width=\"25%\" valign=top><a href=\"os.html\">os</a><br>\n<a href=\"cPickle.html\">cPickle</a><br>\n</td><td width=\"25%\" valign=top><a href=\"re.html\">re</a><br>\n<a href=\"socket.html\">socket</a><br>\n</td><td width=\"25%\" valign=top><a href=\"sys.html\">sys</a><br>\n<a href=\"time.html\">time</a><br>\n</td><td width=\"25%\" valign=top></td></tr></table></td></tr></table><p>\n<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"section\">\n<tr bgcolor=\"#ee77aa\">\n<td colspan=3 valign=bottom>&nbsp;<br>\n<font color=\"#ffffff\" face=\"helvetica, arial\"><big><strong>Classes</strong></big></font></td></tr>\n    \n<tr><td bgcolor=\"#ee77aa\"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>\n<td width=\"100%\"><dl>\n<dt><font face=\"helvetica, arial\"><a href=\"thread.html#_local\">thread._local</a>(<a href=\"__builtin__.html#object\">__builtin__.object</a>)\n</font></dt><dd>\n<dl>\n<dt><font face=\"helvetica, arial\"><a href=\"memcache.html#Client\">Client</a>\n</font></dt></dl>\n</dd>\n</dl>\n <p>\n<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"section\">\n<tr bgcolor=\"#ffc8d8\">\n<td colspan=3 valign=bottom>&nbsp;<br>\n<font color=\"#000000\" face=\"helvetica, arial\"><a name=\"Client\">class <strong>Client</strong></a>(<a href=\"thread.html#_local\">thread._local</a>)</font></td></tr>\n    \n<tr bgcolor=\"#ffc8d8\"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>\n<td colspan=2><tt>Object&nbsp;representing&nbsp;a&nbsp;pool&nbsp;of&nbsp;memcache&nbsp;servers.<br>\n&nbsp;<br>\nSee&nbsp;L{memcache}&nbsp;for&nbsp;an&nbsp;overview.<br>\n&nbsp;<br>\nIn&nbsp;all&nbsp;cases&nbsp;where&nbsp;a&nbsp;key&nbsp;is&nbsp;used,&nbsp;the&nbsp;key&nbsp;can&nbsp;be&nbsp;either:<br>\n&nbsp;&nbsp;&nbsp;&nbsp;1.&nbsp;A&nbsp;simple&nbsp;hashable&nbsp;type&nbsp;(string,&nbsp;integer,&nbsp;etc.).<br>\n&nbsp;&nbsp;&nbsp;&nbsp;2.&nbsp;A&nbsp;tuple&nbsp;of&nbsp;C{(hashvalue,&nbsp;key)}.&nbsp;&nbsp;This&nbsp;is&nbsp;useful&nbsp;if&nbsp;you&nbsp;want&nbsp;to&nbsp;avoid<br>\n&nbsp;&nbsp;&nbsp;&nbsp;making&nbsp;this&nbsp;module&nbsp;calculate&nbsp;a&nbsp;hash&nbsp;value.&nbsp;&nbsp;You&nbsp;may&nbsp;prefer,&nbsp;for<br>\n&nbsp;&nbsp;&nbsp;&nbsp;example,&nbsp;to&nbsp;keep&nbsp;all&nbsp;of&nbsp;a&nbsp;given&nbsp;user's&nbsp;objects&nbsp;on&nbsp;the&nbsp;same&nbsp;memcache<br>\n&nbsp;&nbsp;&nbsp;&nbsp;server,&nbsp;so&nbsp;you&nbsp;could&nbsp;use&nbsp;the&nbsp;user's&nbsp;unique&nbsp;id&nbsp;as&nbsp;the&nbsp;hash&nbsp;value.<br>\n&nbsp;<br>\n@group&nbsp;Setup:&nbsp;__init__,&nbsp;set_servers,&nbsp;forget_dead_hosts,&nbsp;disconnect_all,&nbsp;debuglog<br>\n@group&nbsp;Insertion:&nbsp;set,&nbsp;add,&nbsp;replace,&nbsp;set_multi<br>\n@group&nbsp;Retrieval:&nbsp;get,&nbsp;get_multi<br>\n@group&nbsp;Integers:&nbsp;incr,&nbsp;decr<br>\n@group&nbsp;Removal:&nbsp;delete,&nbsp;delete_multi<br>\n@sort:&nbsp;__init__,&nbsp;set_servers,&nbsp;forget_dead_hosts,&nbsp;disconnect_all,&nbsp;debuglog,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set,&nbsp;set_multi,&nbsp;add,&nbsp;replace,&nbsp;get,&nbsp;get_multi,&nbsp;incr,&nbsp;decr,&nbsp;delete,&nbsp;delete_multi<br>&nbsp;</tt></td></tr>\n<tr><td>&nbsp;</td>\n<td width=\"100%\"><dl><dt>Method resolution order:</dt>\n<dd><a href=\"memcache.html#Client\">Client</a></dd>\n<dd><a href=\"thread.html#_local\">thread._local</a></dd>\n<dd><a href=\"__builtin__.html#object\">__builtin__.object</a></dd>\n</dl>\n<hr>\nMethods defined here:<br>\n<dl><dt><a name=\"Client-__init__\"><strong>__init__</strong></a>(self, servers, debug<font color=\"#909090\">=0</font>, pickleProtocol<font color=\"#909090\">=0</font>, pickler<font color=\"#909090\">=&lt;built-in function Pickler&gt;</font>, unpickler<font color=\"#909090\">=&lt;built-in function Unpickler&gt;</font>, pload<font color=\"#909090\">=None</font>, pid<font color=\"#909090\">=None</font>, server_max_key_length<font color=\"#909090\">=250</font>, server_max_value_length<font color=\"#909090\">=1048576</font>, dead_retry<font color=\"#909090\">=30</font>, socket_timeout<font color=\"#909090\">=3</font>, cache_cas<font color=\"#909090\">=False</font>)</dt><dd><tt>Create&nbsp;a&nbsp;new&nbsp;<a href=\"#Client\">Client</a>&nbsp;object&nbsp;with&nbsp;the&nbsp;given&nbsp;list&nbsp;of&nbsp;servers.<br>\n&nbsp;<br>\n@param&nbsp;servers:&nbsp;C{servers}&nbsp;is&nbsp;passed&nbsp;to&nbsp;L{set_servers}.<br>\n@param&nbsp;debug:&nbsp;whether&nbsp;to&nbsp;display&nbsp;error&nbsp;messages&nbsp;when&nbsp;a&nbsp;server&nbsp;can't&nbsp;be<br>\ncontacted.<br>\n@param&nbsp;pickleProtocol:&nbsp;number&nbsp;to&nbsp;mandate&nbsp;protocol&nbsp;used&nbsp;by&nbsp;(c)Pickle.<br>\n@param&nbsp;pickler:&nbsp;optional&nbsp;override&nbsp;of&nbsp;default&nbsp;Pickler&nbsp;to&nbsp;allow&nbsp;subclassing.<br>\n@param&nbsp;unpickler:&nbsp;optional&nbsp;override&nbsp;of&nbsp;default&nbsp;Unpickler&nbsp;to&nbsp;allow&nbsp;subclassing.<br>\n@param&nbsp;pload:&nbsp;optional&nbsp;persistent_load&nbsp;function&nbsp;to&nbsp;call&nbsp;on&nbsp;pickle&nbsp;loading.<br>\nUseful&nbsp;for&nbsp;cPickle&nbsp;since&nbsp;subclassing&nbsp;isn't&nbsp;allowed.<br>\n@param&nbsp;pid:&nbsp;optional&nbsp;persistent_id&nbsp;function&nbsp;to&nbsp;call&nbsp;on&nbsp;pickle&nbsp;storing.<br>\nUseful&nbsp;for&nbsp;cPickle&nbsp;since&nbsp;subclassing&nbsp;isn't&nbsp;allowed.<br>\n@param&nbsp;dead_retry:&nbsp;number&nbsp;of&nbsp;seconds&nbsp;before&nbsp;retrying&nbsp;a&nbsp;blacklisted<br>\nserver.&nbsp;Default&nbsp;to&nbsp;30&nbsp;s.<br>\n@param&nbsp;socket_timeout:&nbsp;timeout&nbsp;in&nbsp;seconds&nbsp;for&nbsp;all&nbsp;calls&nbsp;to&nbsp;a&nbsp;server.&nbsp;Defaults<br>\nto&nbsp;3&nbsp;seconds.<br>\n@param&nbsp;cache_cas:&nbsp;(default&nbsp;False)&nbsp;If&nbsp;true,&nbsp;cas&nbsp;operations&nbsp;will&nbsp;be<br>\ncached.&nbsp;&nbsp;WARNING:&nbsp;This&nbsp;cache&nbsp;is&nbsp;not&nbsp;expired&nbsp;internally,&nbsp;if&nbsp;you&nbsp;have<br>\na&nbsp;long-running&nbsp;process&nbsp;you&nbsp;will&nbsp;need&nbsp;to&nbsp;expire&nbsp;it&nbsp;manually&nbsp;via<br>\n\"client.<a href=\"#Client-reset_cas\">reset_cas</a>(),&nbsp;or&nbsp;the&nbsp;cache&nbsp;can&nbsp;grow&nbsp;unlimited.<br>\n@param&nbsp;server_max_key_length:&nbsp;(default&nbsp;SERVER_MAX_KEY_LENGTH)<br>\nData&nbsp;that&nbsp;is&nbsp;larger&nbsp;than&nbsp;this&nbsp;will&nbsp;not&nbsp;be&nbsp;sent&nbsp;to&nbsp;the&nbsp;server.<br>\n@param&nbsp;server_max_value_length:&nbsp;(default&nbsp;SERVER_MAX_VALUE_LENGTH)<br>\nData&nbsp;that&nbsp;is&nbsp;larger&nbsp;than&nbsp;this&nbsp;will&nbsp;not&nbsp;be&nbsp;sent&nbsp;to&nbsp;the&nbsp;server.</tt></dd></dl>\n\n<dl><dt><a name=\"Client-add\"><strong>add</strong></a>(self, key, val, time<font color=\"#909090\">=0</font>, min_compress_len<font color=\"#909090\">=0</font>)</dt><dd><tt>Add&nbsp;new&nbsp;key&nbsp;with&nbsp;value.<br>\n&nbsp;<br>\nLike&nbsp;L{set},&nbsp;but&nbsp;only&nbsp;stores&nbsp;in&nbsp;memcache&nbsp;if&nbsp;the&nbsp;key&nbsp;doesn't&nbsp;already&nbsp;exist.<br>\n&nbsp;<br>\n@return:&nbsp;Nonzero&nbsp;on&nbsp;success.<br>\n@rtype:&nbsp;int</tt></dd></dl>\n\n<dl><dt><a name=\"Client-append\"><strong>append</strong></a>(self, key, val, time<font color=\"#909090\">=0</font>, min_compress_len<font color=\"#909090\">=0</font>)</dt><dd><tt>Append&nbsp;the&nbsp;value&nbsp;to&nbsp;the&nbsp;end&nbsp;of&nbsp;the&nbsp;existing&nbsp;key's&nbsp;value.<br>\n&nbsp;<br>\nOnly&nbsp;stores&nbsp;in&nbsp;memcache&nbsp;if&nbsp;key&nbsp;already&nbsp;exists.<br>\nAlso&nbsp;see&nbsp;L{prepend}.<br>\n&nbsp;<br>\n@return:&nbsp;Nonzero&nbsp;on&nbsp;success.<br>\n@rtype:&nbsp;int</tt></dd></dl>\n\n<dl><dt><a name=\"Client-cas\"><strong>cas</strong></a>(self, key, val, time<font color=\"#909090\">=0</font>, min_compress_len<font color=\"#909090\">=0</font>)</dt><dd><tt>Sets&nbsp;a&nbsp;key&nbsp;to&nbsp;a&nbsp;given&nbsp;value&nbsp;in&nbsp;the&nbsp;memcache&nbsp;if&nbsp;it&nbsp;hasn't&nbsp;been<br>\naltered&nbsp;since&nbsp;last&nbsp;fetched.&nbsp;(See&nbsp;L{gets}).<br>\n&nbsp;<br>\nThe&nbsp;C{key}&nbsp;can&nbsp;optionally&nbsp;be&nbsp;an&nbsp;tuple,&nbsp;with&nbsp;the&nbsp;first&nbsp;element<br>\nbeing&nbsp;the&nbsp;server&nbsp;hash&nbsp;value&nbsp;and&nbsp;the&nbsp;second&nbsp;being&nbsp;the&nbsp;key.<br>\nIf&nbsp;you&nbsp;want&nbsp;to&nbsp;avoid&nbsp;making&nbsp;this&nbsp;module&nbsp;calculate&nbsp;a&nbsp;hash&nbsp;value.<br>\nYou&nbsp;may&nbsp;prefer,&nbsp;for&nbsp;example,&nbsp;to&nbsp;keep&nbsp;all&nbsp;of&nbsp;a&nbsp;given&nbsp;user's&nbsp;objects<br>\non&nbsp;the&nbsp;same&nbsp;memcache&nbsp;server,&nbsp;so&nbsp;you&nbsp;could&nbsp;use&nbsp;the&nbsp;user's&nbsp;unique<br>\nid&nbsp;as&nbsp;the&nbsp;hash&nbsp;value.<br>\n&nbsp;<br>\n@return:&nbsp;Nonzero&nbsp;on&nbsp;success.<br>\n@rtype:&nbsp;int<br>\n@param&nbsp;time:&nbsp;Tells&nbsp;memcached&nbsp;the&nbsp;time&nbsp;which&nbsp;this&nbsp;value&nbsp;should&nbsp;expire,<br>\neither&nbsp;as&nbsp;a&nbsp;delta&nbsp;number&nbsp;of&nbsp;seconds,&nbsp;or&nbsp;an&nbsp;absolute&nbsp;unix<br>\ntime-since-the-epoch&nbsp;value.&nbsp;See&nbsp;the&nbsp;memcached&nbsp;protocol&nbsp;docs&nbsp;section<br>\n\"Storage&nbsp;Commands\"&nbsp;for&nbsp;more&nbsp;info&nbsp;on&nbsp;&lt;exptime&gt;.&nbsp;We&nbsp;default&nbsp;to<br>\n0&nbsp;==&nbsp;cache&nbsp;forever.<br>\n@param&nbsp;min_compress_len:&nbsp;The&nbsp;threshold&nbsp;length&nbsp;to&nbsp;kick&nbsp;in<br>\nauto-compression&nbsp;of&nbsp;the&nbsp;value&nbsp;using&nbsp;the&nbsp;zlib.<a href=\"#-compress\">compress</a>()&nbsp;routine.&nbsp;If<br>\nthe&nbsp;value&nbsp;being&nbsp;cached&nbsp;is&nbsp;a&nbsp;string,&nbsp;then&nbsp;the&nbsp;length&nbsp;of&nbsp;the&nbsp;string&nbsp;is<br>\nmeasured,&nbsp;else&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;an&nbsp;object,&nbsp;then&nbsp;the&nbsp;length&nbsp;of&nbsp;the<br>\npickle&nbsp;result&nbsp;is&nbsp;measured.&nbsp;If&nbsp;the&nbsp;resulting&nbsp;attempt&nbsp;at&nbsp;compression<br>\nyeilds&nbsp;a&nbsp;larger&nbsp;string&nbsp;than&nbsp;the&nbsp;input,&nbsp;then&nbsp;it&nbsp;is&nbsp;discarded.&nbsp;For<br>\nbackwards&nbsp;compatability,&nbsp;this&nbsp;parameter&nbsp;defaults&nbsp;to&nbsp;0,&nbsp;indicating<br>\ndon't&nbsp;ever&nbsp;try&nbsp;to&nbsp;compress.</tt></dd></dl>\n\n<dl><dt><a name=\"Client-check_key\"><strong>check_key</strong></a>(self, key, key_extra_len<font color=\"#909090\">=0</font>)</dt><dd><tt>Checks&nbsp;sanity&nbsp;of&nbsp;key.&nbsp;&nbsp;Fails&nbsp;if:<br>\nKey&nbsp;length&nbsp;is&nbsp;&gt;&nbsp;SERVER_MAX_KEY_LENGTH&nbsp;(Raises&nbsp;MemcachedKeyLength).<br>\nContains&nbsp;control&nbsp;characters&nbsp;&nbsp;(Raises&nbsp;MemcachedKeyCharacterError).<br>\nIs&nbsp;not&nbsp;a&nbsp;string&nbsp;(Raises&nbsp;MemcachedStringEncodingError)<br>\nIs&nbsp;an&nbsp;unicode&nbsp;string&nbsp;(Raises&nbsp;MemcachedStringEncodingError)<br>\nIs&nbsp;not&nbsp;a&nbsp;string&nbsp;(Raises&nbsp;MemcachedKeyError)<br>\nIs&nbsp;None&nbsp;(Raises&nbsp;MemcachedKeyError)</tt></dd></dl>\n\n<dl><dt><a name=\"Client-debuglog\"><strong>debuglog</strong></a>(self, str)</dt></dl>\n\n<dl><dt><a name=\"Client-decr\"><strong>decr</strong></a>(self, key, delta<font color=\"#909090\">=1</font>)</dt><dd><tt>Like&nbsp;L{incr},&nbsp;but&nbsp;decrements.&nbsp;&nbsp;Unlike&nbsp;L{incr},&nbsp;underflow&nbsp;is&nbsp;checked&nbsp;and<br>\nnew&nbsp;values&nbsp;are&nbsp;capped&nbsp;at&nbsp;0.&nbsp;&nbsp;If&nbsp;server&nbsp;value&nbsp;is&nbsp;1,&nbsp;a&nbsp;decrement&nbsp;of&nbsp;2<br>\nreturns&nbsp;0,&nbsp;not&nbsp;-1.<br>\n&nbsp;<br>\n@param&nbsp;delta:&nbsp;Integer&nbsp;amount&nbsp;to&nbsp;decrement&nbsp;by&nbsp;(should&nbsp;be&nbsp;zero&nbsp;or&nbsp;greater).<br>\n@return:&nbsp;New&nbsp;value&nbsp;after&nbsp;decrementing.<br>\n@rtype:&nbsp;int</tt></dd></dl>\n\n<dl><dt><a name=\"Client-delete\"><strong>delete</strong></a>(self, key, time<font color=\"#909090\">=0</font>)</dt><dd><tt>Deletes&nbsp;a&nbsp;key&nbsp;from&nbsp;the&nbsp;memcache.<br>\n&nbsp;<br>\n@return:&nbsp;Nonzero&nbsp;on&nbsp;success.<br>\n@param&nbsp;time:&nbsp;number&nbsp;of&nbsp;seconds&nbsp;any&nbsp;subsequent&nbsp;set&nbsp;/&nbsp;update&nbsp;commands<br>\nshould&nbsp;fail.&nbsp;Defaults&nbsp;to&nbsp;None&nbsp;for&nbsp;no&nbsp;delay.<br>\n@rtype:&nbsp;int</tt></dd></dl>\n\n<dl><dt><a name=\"Client-delete_multi\"><strong>delete_multi</strong></a>(self, keys, time<font color=\"#909090\">=0</font>, key_prefix<font color=\"#909090\">=''</font>)</dt><dd><tt>Delete&nbsp;multiple&nbsp;keys&nbsp;in&nbsp;the&nbsp;memcache&nbsp;doing&nbsp;just&nbsp;one&nbsp;query.<br>\n&nbsp;<br>\n&gt;&gt;&gt;&nbsp;notset_keys&nbsp;=&nbsp;mc.<a href=\"#Client-set_multi\">set_multi</a>({'key1'&nbsp;:&nbsp;'val1',&nbsp;'key2'&nbsp;:&nbsp;'val2'})<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-get_multi\">get_multi</a>(['key1',&nbsp;'key2'])&nbsp;==&nbsp;{'key1'&nbsp;:&nbsp;'val1',&nbsp;'key2'&nbsp;:&nbsp;'val2'}<br>\n1<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-delete_multi\">delete_multi</a>(['key1',&nbsp;'key2'])<br>\n1<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-get_multi\">get_multi</a>(['key1',&nbsp;'key2'])&nbsp;==&nbsp;{}<br>\n1<br>\n&nbsp;<br>\n&nbsp;<br>\nThis&nbsp;method&nbsp;is&nbsp;recommended&nbsp;over&nbsp;iterated&nbsp;regular&nbsp;L{delete}s&nbsp;as&nbsp;it&nbsp;reduces&nbsp;total&nbsp;latency,&nbsp;since<br>\nyour&nbsp;app&nbsp;doesn't&nbsp;have&nbsp;to&nbsp;wait&nbsp;for&nbsp;each&nbsp;round-trip&nbsp;of&nbsp;L{delete}&nbsp;before&nbsp;sending<br>\nthe&nbsp;next&nbsp;one.<br>\n&nbsp;<br>\n@param&nbsp;keys:&nbsp;An&nbsp;iterable&nbsp;of&nbsp;keys&nbsp;to&nbsp;clear<br>\n@param&nbsp;time:&nbsp;number&nbsp;of&nbsp;seconds&nbsp;any&nbsp;subsequent&nbsp;set&nbsp;/&nbsp;update&nbsp;commands&nbsp;should&nbsp;fail.&nbsp;Defaults&nbsp;to&nbsp;0&nbsp;for&nbsp;no&nbsp;delay.<br>\n@param&nbsp;key_prefix:&nbsp;&nbsp;Optional&nbsp;string&nbsp;to&nbsp;prepend&nbsp;to&nbsp;each&nbsp;key&nbsp;when&nbsp;sending&nbsp;to&nbsp;memcache.<br>\n&nbsp;&nbsp;&nbsp;&nbsp;See&nbsp;docs&nbsp;for&nbsp;L{get_multi}&nbsp;and&nbsp;L{set_multi}.<br>\n&nbsp;<br>\n@return:&nbsp;1&nbsp;if&nbsp;no&nbsp;failure&nbsp;in&nbsp;communication&nbsp;with&nbsp;any&nbsp;memcacheds.<br>\n@rtype:&nbsp;int</tt></dd></dl>\n\n<dl><dt><a name=\"Client-disconnect_all\"><strong>disconnect_all</strong></a>(self)</dt></dl>\n\n<dl><dt><a name=\"Client-flush_all\"><strong>flush_all</strong></a>(self)</dt><dd><tt>Expire&nbsp;all&nbsp;data&nbsp;currently&nbsp;in&nbsp;the&nbsp;memcache&nbsp;servers.</tt></dd></dl>\n\n<dl><dt><a name=\"Client-forget_dead_hosts\"><strong>forget_dead_hosts</strong></a>(self)</dt><dd><tt>Reset&nbsp;every&nbsp;host&nbsp;in&nbsp;the&nbsp;pool&nbsp;to&nbsp;an&nbsp;\"alive\"&nbsp;state.</tt></dd></dl>\n\n<dl><dt><a name=\"Client-get\"><strong>get</strong></a>(self, key)</dt><dd><tt>Retrieves&nbsp;a&nbsp;key&nbsp;from&nbsp;the&nbsp;memcache.<br>\n&nbsp;<br>\n@return:&nbsp;The&nbsp;value&nbsp;or&nbsp;None.</tt></dd></dl>\n\n<dl><dt><a name=\"Client-get_multi\"><strong>get_multi</strong></a>(self, keys, key_prefix<font color=\"#909090\">=''</font>)</dt><dd><tt>Retrieves&nbsp;multiple&nbsp;keys&nbsp;from&nbsp;the&nbsp;memcache&nbsp;doing&nbsp;just&nbsp;one&nbsp;query.<br>\n&nbsp;<br>\n&gt;&gt;&gt;&nbsp;success&nbsp;=&nbsp;mc.<a href=\"#Client-set\">set</a>(\"foo\",&nbsp;\"bar\")<br>\n&gt;&gt;&gt;&nbsp;success&nbsp;=&nbsp;mc.<a href=\"#Client-set\">set</a>(\"baz\",&nbsp;42)<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-get_multi\">get_multi</a>([\"foo\",&nbsp;\"baz\",&nbsp;\"foobar\"])&nbsp;==&nbsp;{\"foo\":&nbsp;\"bar\",&nbsp;\"baz\":&nbsp;42}<br>\n1<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-set_multi\">set_multi</a>({'k1'&nbsp;:&nbsp;1,&nbsp;'k2'&nbsp;:&nbsp;2},&nbsp;key_prefix='pfx_')&nbsp;==&nbsp;[]<br>\n1<br>\n&nbsp;<br>\nThis&nbsp;looks&nbsp;up&nbsp;keys&nbsp;'pfx_k1',&nbsp;'pfx_k2',&nbsp;...&nbsp;.&nbsp;Returned&nbsp;dict&nbsp;will&nbsp;just&nbsp;have&nbsp;unprefixed&nbsp;keys&nbsp;'k1',&nbsp;'k2'.<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-get_multi\">get_multi</a>(['k1',&nbsp;'k2',&nbsp;'nonexist'],&nbsp;key_prefix='pfx_')&nbsp;==&nbsp;{'k1'&nbsp;:&nbsp;1,&nbsp;'k2'&nbsp;:&nbsp;2}<br>\n1<br>\n&nbsp;<br>\nget_mult&nbsp;[&nbsp;and&nbsp;L{set_multi}&nbsp;]&nbsp;can&nbsp;take&nbsp;str()-ables&nbsp;like&nbsp;ints&nbsp;/&nbsp;longs&nbsp;as&nbsp;keys&nbsp;too.&nbsp;Such&nbsp;as&nbsp;your&nbsp;db&nbsp;pri&nbsp;key&nbsp;fields.<br>\nThey're&nbsp;rotored&nbsp;through&nbsp;str()&nbsp;before&nbsp;being&nbsp;passed&nbsp;off&nbsp;to&nbsp;memcache,&nbsp;with&nbsp;or&nbsp;without&nbsp;the&nbsp;use&nbsp;of&nbsp;a&nbsp;key_prefix.<br>\nIn&nbsp;this&nbsp;mode,&nbsp;the&nbsp;key_prefix&nbsp;could&nbsp;be&nbsp;a&nbsp;table&nbsp;name,&nbsp;and&nbsp;the&nbsp;key&nbsp;itself&nbsp;a&nbsp;db&nbsp;primary&nbsp;key&nbsp;number.<br>\n&nbsp;<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-set_multi\">set_multi</a>({42:&nbsp;'douglass&nbsp;adams',&nbsp;46&nbsp;:&nbsp;'and&nbsp;2&nbsp;just&nbsp;ahead&nbsp;of&nbsp;me'},&nbsp;key_prefix='numkeys_')&nbsp;==&nbsp;[]<br>\n1<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-get_multi\">get_multi</a>([46,&nbsp;42],&nbsp;key_prefix='numkeys_')&nbsp;==&nbsp;{42:&nbsp;'douglass&nbsp;adams',&nbsp;46&nbsp;:&nbsp;'and&nbsp;2&nbsp;just&nbsp;ahead&nbsp;of&nbsp;me'}<br>\n1<br>\n&nbsp;<br>\nThis&nbsp;method&nbsp;is&nbsp;recommended&nbsp;over&nbsp;regular&nbsp;L{get}&nbsp;as&nbsp;it&nbsp;lowers&nbsp;the&nbsp;number&nbsp;of<br>\ntotal&nbsp;packets&nbsp;flying&nbsp;around&nbsp;your&nbsp;network,&nbsp;reducing&nbsp;total&nbsp;latency,&nbsp;since<br>\nyour&nbsp;app&nbsp;doesn't&nbsp;have&nbsp;to&nbsp;wait&nbsp;for&nbsp;each&nbsp;round-trip&nbsp;of&nbsp;L{get}&nbsp;before&nbsp;sending<br>\nthe&nbsp;next&nbsp;one.<br>\n&nbsp;<br>\nSee&nbsp;also&nbsp;L{set_multi}.<br>\n&nbsp;<br>\n@param&nbsp;keys:&nbsp;An&nbsp;array&nbsp;of&nbsp;keys.<br>\n@param&nbsp;key_prefix:&nbsp;A&nbsp;string&nbsp;to&nbsp;prefix&nbsp;each&nbsp;key&nbsp;when&nbsp;we&nbsp;communicate&nbsp;with&nbsp;memcache.<br>\n&nbsp;&nbsp;&nbsp;&nbsp;Facilitates&nbsp;pseudo-namespaces&nbsp;within&nbsp;memcache.&nbsp;Returned&nbsp;dictionary&nbsp;keys&nbsp;will&nbsp;not&nbsp;have&nbsp;this&nbsp;prefix.<br>\n@return:&nbsp;&nbsp;A&nbsp;dictionary&nbsp;of&nbsp;key/value&nbsp;pairs&nbsp;that&nbsp;were&nbsp;available.&nbsp;If&nbsp;key_prefix&nbsp;was&nbsp;provided,&nbsp;the&nbsp;keys&nbsp;in&nbsp;the&nbsp;retured&nbsp;dictionary&nbsp;will&nbsp;not&nbsp;have&nbsp;it&nbsp;present.</tt></dd></dl>\n\n<dl><dt><a name=\"Client-get_slabs\"><strong>get_slabs</strong></a>(self)</dt></dl>\n\n<dl><dt><a name=\"Client-get_stats\"><strong>get_stats</strong></a>(self, stat_args<font color=\"#909090\">=None</font>)</dt><dd><tt>Get&nbsp;statistics&nbsp;from&nbsp;each&nbsp;of&nbsp;the&nbsp;servers.<br>\n&nbsp;<br>\n@param&nbsp;stat_args:&nbsp;Additional&nbsp;arguments&nbsp;to&nbsp;pass&nbsp;to&nbsp;the&nbsp;memcache<br>\n&nbsp;&nbsp;&nbsp;&nbsp;\"stats\"&nbsp;command.<br>\n&nbsp;<br>\n@return:&nbsp;A&nbsp;list&nbsp;of&nbsp;tuples&nbsp;(&nbsp;server_identifier,&nbsp;stats_dictionary&nbsp;).<br>\n&nbsp;&nbsp;&nbsp;&nbsp;The&nbsp;dictionary&nbsp;contains&nbsp;a&nbsp;number&nbsp;of&nbsp;name/value&nbsp;pairs&nbsp;specifying<br>\n&nbsp;&nbsp;&nbsp;&nbsp;the&nbsp;name&nbsp;of&nbsp;the&nbsp;status&nbsp;field&nbsp;and&nbsp;the&nbsp;string&nbsp;value&nbsp;associated&nbsp;with<br>\n&nbsp;&nbsp;&nbsp;&nbsp;it.&nbsp;&nbsp;The&nbsp;values&nbsp;are&nbsp;not&nbsp;converted&nbsp;from&nbsp;strings.</tt></dd></dl>\n\n<dl><dt><a name=\"Client-gets\"><strong>gets</strong></a>(self, key)</dt><dd><tt>Retrieves&nbsp;a&nbsp;key&nbsp;from&nbsp;the&nbsp;memcache.&nbsp;Used&nbsp;in&nbsp;conjunction&nbsp;with&nbsp;'cas'.<br>\n&nbsp;<br>\n@return:&nbsp;The&nbsp;value&nbsp;or&nbsp;None.</tt></dd></dl>\n\n<dl><dt><a name=\"Client-incr\"><strong>incr</strong></a>(self, key, delta<font color=\"#909090\">=1</font>)</dt><dd><tt>Sends&nbsp;a&nbsp;command&nbsp;to&nbsp;the&nbsp;server&nbsp;to&nbsp;atomically&nbsp;increment&nbsp;the&nbsp;value<br>\nfor&nbsp;C{key}&nbsp;by&nbsp;C{delta},&nbsp;or&nbsp;by&nbsp;1&nbsp;if&nbsp;C{delta}&nbsp;is&nbsp;unspecified.<br>\nReturns&nbsp;None&nbsp;if&nbsp;C{key}&nbsp;doesn't&nbsp;exist&nbsp;on&nbsp;server,&nbsp;otherwise&nbsp;it<br>\nreturns&nbsp;the&nbsp;new&nbsp;value&nbsp;after&nbsp;incrementing.<br>\n&nbsp;<br>\nNote&nbsp;that&nbsp;the&nbsp;value&nbsp;for&nbsp;C{key}&nbsp;must&nbsp;already&nbsp;exist&nbsp;in&nbsp;the&nbsp;memcache,<br>\nand&nbsp;it&nbsp;must&nbsp;be&nbsp;the&nbsp;string&nbsp;representation&nbsp;of&nbsp;an&nbsp;integer.<br>\n&nbsp;<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-set\">set</a>(\"counter\",&nbsp;\"20\")&nbsp;&nbsp;#&nbsp;returns&nbsp;1,&nbsp;indicating&nbsp;success<br>\n1<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-incr\">incr</a>(\"counter\")<br>\n21<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-incr\">incr</a>(\"counter\")<br>\n22<br>\n&nbsp;<br>\nOverflow&nbsp;on&nbsp;server&nbsp;is&nbsp;not&nbsp;checked.&nbsp;&nbsp;Be&nbsp;aware&nbsp;of&nbsp;values&nbsp;approaching<br>\n2**32.&nbsp;&nbsp;See&nbsp;L{decr}.<br>\n&nbsp;<br>\n@param&nbsp;delta:&nbsp;Integer&nbsp;amount&nbsp;to&nbsp;increment&nbsp;by&nbsp;(should&nbsp;be&nbsp;zero&nbsp;or&nbsp;greater).<br>\n@return:&nbsp;New&nbsp;value&nbsp;after&nbsp;incrementing.<br>\n@rtype:&nbsp;int</tt></dd></dl>\n\n<dl><dt><a name=\"Client-prepend\"><strong>prepend</strong></a>(self, key, val, time<font color=\"#909090\">=0</font>, min_compress_len<font color=\"#909090\">=0</font>)</dt><dd><tt>Prepend&nbsp;the&nbsp;value&nbsp;to&nbsp;the&nbsp;beginning&nbsp;of&nbsp;the&nbsp;existing&nbsp;key's&nbsp;value.<br>\n&nbsp;<br>\nOnly&nbsp;stores&nbsp;in&nbsp;memcache&nbsp;if&nbsp;key&nbsp;already&nbsp;exists.<br>\nAlso&nbsp;see&nbsp;L{append}.<br>\n&nbsp;<br>\n@return:&nbsp;Nonzero&nbsp;on&nbsp;success.<br>\n@rtype:&nbsp;int</tt></dd></dl>\n\n<dl><dt><a name=\"Client-replace\"><strong>replace</strong></a>(self, key, val, time<font color=\"#909090\">=0</font>, min_compress_len<font color=\"#909090\">=0</font>)</dt><dd><tt>Replace&nbsp;existing&nbsp;key&nbsp;with&nbsp;value.<br>\n&nbsp;<br>\nLike&nbsp;L{set},&nbsp;but&nbsp;only&nbsp;stores&nbsp;in&nbsp;memcache&nbsp;if&nbsp;the&nbsp;key&nbsp;already&nbsp;exists.<br>\nThe&nbsp;opposite&nbsp;of&nbsp;L{add}.<br>\n&nbsp;<br>\n@return:&nbsp;Nonzero&nbsp;on&nbsp;success.<br>\n@rtype:&nbsp;int</tt></dd></dl>\n\n<dl><dt><a name=\"Client-reset_cas\"><strong>reset_cas</strong></a>(self)</dt><dd><tt>Reset&nbsp;the&nbsp;cas&nbsp;cache.&nbsp;&nbsp;This&nbsp;is&nbsp;only&nbsp;used&nbsp;if&nbsp;the&nbsp;<a href=\"#Client\">Client</a>()&nbsp;object<br>\nwas&nbsp;created&nbsp;with&nbsp;\"cache_cas=True\".&nbsp;&nbsp;If&nbsp;used,&nbsp;this&nbsp;cache&nbsp;does&nbsp;not<br>\nexpire&nbsp;internally,&nbsp;so&nbsp;it&nbsp;can&nbsp;grow&nbsp;unbounded&nbsp;if&nbsp;you&nbsp;do&nbsp;not&nbsp;clear&nbsp;it<br>\nyourself.</tt></dd></dl>\n\n<dl><dt><a name=\"Client-set\"><strong>set</strong></a>(self, key, val, time<font color=\"#909090\">=0</font>, min_compress_len<font color=\"#909090\">=0</font>)</dt><dd><tt>Unconditionally&nbsp;sets&nbsp;a&nbsp;key&nbsp;to&nbsp;a&nbsp;given&nbsp;value&nbsp;in&nbsp;the&nbsp;memcache.<br>\n&nbsp;<br>\nThe&nbsp;C{key}&nbsp;can&nbsp;optionally&nbsp;be&nbsp;an&nbsp;tuple,&nbsp;with&nbsp;the&nbsp;first&nbsp;element<br>\nbeing&nbsp;the&nbsp;server&nbsp;hash&nbsp;value&nbsp;and&nbsp;the&nbsp;second&nbsp;being&nbsp;the&nbsp;key.<br>\nIf&nbsp;you&nbsp;want&nbsp;to&nbsp;avoid&nbsp;making&nbsp;this&nbsp;module&nbsp;calculate&nbsp;a&nbsp;hash&nbsp;value.<br>\nYou&nbsp;may&nbsp;prefer,&nbsp;for&nbsp;example,&nbsp;to&nbsp;keep&nbsp;all&nbsp;of&nbsp;a&nbsp;given&nbsp;user's&nbsp;objects<br>\non&nbsp;the&nbsp;same&nbsp;memcache&nbsp;server,&nbsp;so&nbsp;you&nbsp;could&nbsp;use&nbsp;the&nbsp;user's&nbsp;unique<br>\nid&nbsp;as&nbsp;the&nbsp;hash&nbsp;value.<br>\n&nbsp;<br>\n@return:&nbsp;Nonzero&nbsp;on&nbsp;success.<br>\n@rtype:&nbsp;int<br>\n@param&nbsp;time:&nbsp;Tells&nbsp;memcached&nbsp;the&nbsp;time&nbsp;which&nbsp;this&nbsp;value&nbsp;should&nbsp;expire,&nbsp;either<br>\nas&nbsp;a&nbsp;delta&nbsp;number&nbsp;of&nbsp;seconds,&nbsp;or&nbsp;an&nbsp;absolute&nbsp;unix&nbsp;time-since-the-epoch<br>\nvalue.&nbsp;See&nbsp;the&nbsp;memcached&nbsp;protocol&nbsp;docs&nbsp;section&nbsp;\"Storage&nbsp;Commands\"<br>\nfor&nbsp;more&nbsp;info&nbsp;on&nbsp;&lt;exptime&gt;.&nbsp;We&nbsp;default&nbsp;to&nbsp;0&nbsp;==&nbsp;cache&nbsp;forever.<br>\n@param&nbsp;min_compress_len:&nbsp;The&nbsp;threshold&nbsp;length&nbsp;to&nbsp;kick&nbsp;in&nbsp;auto-compression<br>\nof&nbsp;the&nbsp;value&nbsp;using&nbsp;the&nbsp;zlib.<a href=\"#-compress\">compress</a>()&nbsp;routine.&nbsp;If&nbsp;the&nbsp;value&nbsp;being&nbsp;cached&nbsp;is<br>\na&nbsp;string,&nbsp;then&nbsp;the&nbsp;length&nbsp;of&nbsp;the&nbsp;string&nbsp;is&nbsp;measured,&nbsp;else&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;an<br>\nobject,&nbsp;then&nbsp;the&nbsp;length&nbsp;of&nbsp;the&nbsp;pickle&nbsp;result&nbsp;is&nbsp;measured.&nbsp;If&nbsp;the&nbsp;resulting<br>\nattempt&nbsp;at&nbsp;compression&nbsp;yeilds&nbsp;a&nbsp;larger&nbsp;string&nbsp;than&nbsp;the&nbsp;input,&nbsp;then&nbsp;it&nbsp;is<br>\ndiscarded.&nbsp;For&nbsp;backwards&nbsp;compatability,&nbsp;this&nbsp;parameter&nbsp;defaults&nbsp;to&nbsp;0,<br>\nindicating&nbsp;don't&nbsp;ever&nbsp;try&nbsp;to&nbsp;compress.</tt></dd></dl>\n\n<dl><dt><a name=\"Client-set_multi\"><strong>set_multi</strong></a>(self, mapping, time<font color=\"#909090\">=0</font>, key_prefix<font color=\"#909090\">=''</font>, min_compress_len<font color=\"#909090\">=0</font>)</dt><dd><tt>Sets&nbsp;multiple&nbsp;keys&nbsp;in&nbsp;the&nbsp;memcache&nbsp;doing&nbsp;just&nbsp;one&nbsp;query.<br>\n&nbsp;<br>\n&gt;&gt;&gt;&nbsp;notset_keys&nbsp;=&nbsp;mc.<a href=\"#Client-set_multi\">set_multi</a>({'key1'&nbsp;:&nbsp;'val1',&nbsp;'key2'&nbsp;:&nbsp;'val2'})<br>\n&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-get_multi\">get_multi</a>(['key1',&nbsp;'key2'])&nbsp;==&nbsp;{'key1'&nbsp;:&nbsp;'val1',&nbsp;'key2'&nbsp;:&nbsp;'val2'}<br>\n1<br>\n&nbsp;<br>\n&nbsp;<br>\nThis&nbsp;method&nbsp;is&nbsp;recommended&nbsp;over&nbsp;regular&nbsp;L{set}&nbsp;as&nbsp;it&nbsp;lowers&nbsp;the&nbsp;number&nbsp;of<br>\ntotal&nbsp;packets&nbsp;flying&nbsp;around&nbsp;your&nbsp;network,&nbsp;reducing&nbsp;total&nbsp;latency,&nbsp;since<br>\nyour&nbsp;app&nbsp;doesn't&nbsp;have&nbsp;to&nbsp;wait&nbsp;for&nbsp;each&nbsp;round-trip&nbsp;of&nbsp;L{set}&nbsp;before&nbsp;sending<br>\nthe&nbsp;next&nbsp;one.<br>\n&nbsp;<br>\n@param&nbsp;mapping:&nbsp;A&nbsp;dict&nbsp;of&nbsp;key/value&nbsp;pairs&nbsp;to&nbsp;set.<br>\n@param&nbsp;time:&nbsp;Tells&nbsp;memcached&nbsp;the&nbsp;time&nbsp;which&nbsp;this&nbsp;value&nbsp;should&nbsp;expire,&nbsp;either<br>\nas&nbsp;a&nbsp;delta&nbsp;number&nbsp;of&nbsp;seconds,&nbsp;or&nbsp;an&nbsp;absolute&nbsp;unix&nbsp;time-since-the-epoch<br>\nvalue.&nbsp;See&nbsp;the&nbsp;memcached&nbsp;protocol&nbsp;docs&nbsp;section&nbsp;\"Storage&nbsp;Commands\"<br>\nfor&nbsp;more&nbsp;info&nbsp;on&nbsp;&lt;exptime&gt;.&nbsp;We&nbsp;default&nbsp;to&nbsp;0&nbsp;==&nbsp;cache&nbsp;forever.<br>\n@param&nbsp;key_prefix:&nbsp;&nbsp;Optional&nbsp;string&nbsp;to&nbsp;prepend&nbsp;to&nbsp;each&nbsp;key&nbsp;when&nbsp;sending&nbsp;to&nbsp;memcache.&nbsp;Allows&nbsp;you&nbsp;to&nbsp;efficiently&nbsp;stuff&nbsp;these&nbsp;keys&nbsp;into&nbsp;a&nbsp;pseudo-namespace&nbsp;in&nbsp;memcache:<br>\n&nbsp;&nbsp;&nbsp;&nbsp;&gt;&gt;&gt;&nbsp;notset_keys&nbsp;=&nbsp;mc.<a href=\"#Client-set_multi\">set_multi</a>({'key1'&nbsp;:&nbsp;'val1',&nbsp;'key2'&nbsp;:&nbsp;'val2'},&nbsp;key_prefix='subspace_')<br>\n&nbsp;&nbsp;&nbsp;&nbsp;&gt;&gt;&gt;&nbsp;len(notset_keys)&nbsp;==&nbsp;0<br>\n&nbsp;&nbsp;&nbsp;&nbsp;True<br>\n&nbsp;&nbsp;&nbsp;&nbsp;&gt;&gt;&gt;&nbsp;mc.<a href=\"#Client-get_multi\">get_multi</a>(['subspace_key1',&nbsp;'subspace_key2'])&nbsp;==&nbsp;{'subspace_key1'&nbsp;:&nbsp;'val1',&nbsp;'subspace_key2'&nbsp;:&nbsp;'val2'}<br>\n&nbsp;&nbsp;&nbsp;&nbsp;True<br>\n&nbsp;<br>\n&nbsp;&nbsp;&nbsp;&nbsp;Causes&nbsp;key&nbsp;'subspace_key1'&nbsp;and&nbsp;'subspace_key2'&nbsp;to&nbsp;be&nbsp;set.&nbsp;Useful&nbsp;in&nbsp;conjunction&nbsp;with&nbsp;a&nbsp;higher-level&nbsp;layer&nbsp;which&nbsp;applies&nbsp;namespaces&nbsp;to&nbsp;data&nbsp;in&nbsp;memcache.<br>\n&nbsp;&nbsp;&nbsp;&nbsp;In&nbsp;this&nbsp;case,&nbsp;the&nbsp;return&nbsp;result&nbsp;would&nbsp;be&nbsp;the&nbsp;list&nbsp;of&nbsp;notset&nbsp;original&nbsp;keys,&nbsp;prefix&nbsp;not&nbsp;applied.<br>\n&nbsp;<br>\n@param&nbsp;min_compress_len:&nbsp;The&nbsp;threshold&nbsp;length&nbsp;to&nbsp;kick&nbsp;in&nbsp;auto-compression<br>\nof&nbsp;the&nbsp;value&nbsp;using&nbsp;the&nbsp;zlib.<a href=\"#-compress\">compress</a>()&nbsp;routine.&nbsp;If&nbsp;the&nbsp;value&nbsp;being&nbsp;cached&nbsp;is<br>\na&nbsp;string,&nbsp;then&nbsp;the&nbsp;length&nbsp;of&nbsp;the&nbsp;string&nbsp;is&nbsp;measured,&nbsp;else&nbsp;if&nbsp;the&nbsp;value&nbsp;is&nbsp;an<br>\nobject,&nbsp;then&nbsp;the&nbsp;length&nbsp;of&nbsp;the&nbsp;pickle&nbsp;result&nbsp;is&nbsp;measured.&nbsp;If&nbsp;the&nbsp;resulting<br>\nattempt&nbsp;at&nbsp;compression&nbsp;yeilds&nbsp;a&nbsp;larger&nbsp;string&nbsp;than&nbsp;the&nbsp;input,&nbsp;then&nbsp;it&nbsp;is<br>\ndiscarded.&nbsp;For&nbsp;backwards&nbsp;compatability,&nbsp;this&nbsp;parameter&nbsp;defaults&nbsp;to&nbsp;0,<br>\nindicating&nbsp;don't&nbsp;ever&nbsp;try&nbsp;to&nbsp;compress.<br>\n@return:&nbsp;List&nbsp;of&nbsp;keys&nbsp;which&nbsp;failed&nbsp;to&nbsp;be&nbsp;stored&nbsp;[&nbsp;memcache&nbsp;out&nbsp;of&nbsp;memory,&nbsp;etc.&nbsp;].<br>\n@rtype:&nbsp;list</tt></dd></dl>\n\n<dl><dt><a name=\"Client-set_servers\"><strong>set_servers</strong></a>(self, servers)</dt><dd><tt>Set&nbsp;the&nbsp;pool&nbsp;of&nbsp;servers&nbsp;used&nbsp;by&nbsp;this&nbsp;client.<br>\n&nbsp;<br>\n@param&nbsp;servers:&nbsp;an&nbsp;array&nbsp;of&nbsp;servers.<br>\nServers&nbsp;can&nbsp;be&nbsp;passed&nbsp;in&nbsp;two&nbsp;forms:<br>\n&nbsp;&nbsp;&nbsp;&nbsp;1.&nbsp;Strings&nbsp;of&nbsp;the&nbsp;form&nbsp;C{\"host:port\"},&nbsp;which&nbsp;implies&nbsp;a&nbsp;default&nbsp;weight&nbsp;of&nbsp;1.<br>\n&nbsp;&nbsp;&nbsp;&nbsp;2.&nbsp;Tuples&nbsp;of&nbsp;the&nbsp;form&nbsp;C{(\"host:port\",&nbsp;weight)},&nbsp;where&nbsp;C{weight}&nbsp;is<br>\n&nbsp;&nbsp;&nbsp;&nbsp;an&nbsp;integer&nbsp;weight&nbsp;value.</tt></dd></dl>\n\n<hr>\nData descriptors defined here:<br>\n<dl><dt><strong>__dict__</strong></dt>\n<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>\n</dl>\n<hr>\nData and other attributes defined here:<br>\n<dl><dt><strong>MemcachedKeyCharacterError</strong> = &lt;class 'memcache.MemcachedKeyCharacterError'&gt;</dl>\n\n<dl><dt><strong>MemcachedKeyError</strong> = &lt;class 'memcache.MemcachedKeyError'&gt;</dl>\n\n<dl><dt><strong>MemcachedKeyLengthError</strong> = &lt;class 'memcache.MemcachedKeyLengthError'&gt;</dl>\n\n<dl><dt><strong>MemcachedKeyNoneError</strong> = &lt;class 'memcache.MemcachedKeyNoneError'&gt;</dl>\n\n<dl><dt><strong>MemcachedKeyTypeError</strong> = &lt;class 'memcache.MemcachedKeyTypeError'&gt;</dl>\n\n<dl><dt><strong>MemcachedStringEncodingError</strong> = &lt;class 'memcache.MemcachedStringEncodingError'&gt;</dl>\n\n<hr>\nMethods inherited from <a href=\"thread.html#_local\">thread._local</a>:<br>\n<dl><dt><a name=\"Client-__delattr__\"><strong>__delattr__</strong></a>(...)</dt><dd><tt>x.<a href=\"#Client-__delattr__\">__delattr__</a>('name')&nbsp;&lt;==&gt;&nbsp;del&nbsp;x.name</tt></dd></dl>\n\n<dl><dt><a name=\"Client-__getattribute__\"><strong>__getattribute__</strong></a>(...)</dt><dd><tt>x.<a href=\"#Client-__getattribute__\">__getattribute__</a>('name')&nbsp;&lt;==&gt;&nbsp;x.name</tt></dd></dl>\n\n<dl><dt><a name=\"Client-__setattr__\"><strong>__setattr__</strong></a>(...)</dt><dd><tt>x.<a href=\"#Client-__setattr__\">__setattr__</a>('name',&nbsp;value)&nbsp;&lt;==&gt;&nbsp;x.name&nbsp;=&nbsp;value</tt></dd></dl>\n\n<hr>\nData and other attributes inherited from <a href=\"thread.html#_local\">thread._local</a>:<br>\n<dl><dt><strong>__new__</strong> = &lt;built-in method __new__ of type object&gt;<dd><tt>T.<a href=\"#Client-__new__\">__new__</a>(S,&nbsp;...)&nbsp;-&gt;&nbsp;a&nbsp;new&nbsp;object&nbsp;with&nbsp;type&nbsp;S,&nbsp;a&nbsp;subtype&nbsp;of&nbsp;T</tt></dl>\n\n</td></tr></table></td></tr></table><p>\n<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"section\">\n<tr bgcolor=\"#eeaa77\">\n<td colspan=3 valign=bottom>&nbsp;<br>\n<font color=\"#ffffff\" face=\"helvetica, arial\"><big><strong>Functions</strong></big></font></td></tr>\n    \n<tr><td bgcolor=\"#eeaa77\"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>\n<td width=\"100%\"><dl><dt><a name=\"-StringIO\"><strong>StringIO</strong></a>(...)</dt><dd><tt><a href=\"#-StringIO\">StringIO</a>([s])&nbsp;--&nbsp;Return&nbsp;a&nbsp;StringIO-like&nbsp;stream&nbsp;for&nbsp;reading&nbsp;or&nbsp;writing</tt></dd></dl>\n <dl><dt><a name=\"-cmemcache_hash\"><strong>cmemcache_hash</strong></a>(key)</dt></dl>\n <dl><dt><a name=\"-compress\"><strong>compress</strong></a>(...)</dt><dd><tt><a href=\"#-compress\">compress</a>(string[,&nbsp;level])&nbsp;--&nbsp;Returned&nbsp;compressed&nbsp;string.<br>\n&nbsp;<br>\nOptional&nbsp;arg&nbsp;level&nbsp;is&nbsp;the&nbsp;compression&nbsp;level,&nbsp;in&nbsp;1-9.</tt></dd></dl>\n <dl><dt><a name=\"-crc32\"><strong>crc32</strong></a>(...)</dt><dd><tt>(data,&nbsp;oldcrc&nbsp;=&nbsp;0)&nbsp;-&gt;&nbsp;newcrc.&nbsp;Compute&nbsp;CRC-32&nbsp;incrementally</tt></dd></dl>\n <dl><dt><a name=\"-decompress\"><strong>decompress</strong></a>(...)</dt><dd><tt><a href=\"#-decompress\">decompress</a>(string[,&nbsp;wbits[,&nbsp;bufsize]])&nbsp;--&nbsp;Return&nbsp;decompressed&nbsp;string.<br>\n&nbsp;<br>\nOptional&nbsp;arg&nbsp;wbits&nbsp;is&nbsp;the&nbsp;window&nbsp;buffer&nbsp;size.&nbsp;&nbsp;Optional&nbsp;arg&nbsp;bufsize&nbsp;is<br>\nthe&nbsp;initial&nbsp;output&nbsp;buffer&nbsp;size.</tt></dd></dl>\n <dl><dt><a name=\"-serverHashFunction\"><strong>serverHashFunction</strong></a> = cmemcache_hash(key)</dt></dl>\n <dl><dt><a name=\"-useOldServerHashFunction\"><strong>useOldServerHashFunction</strong></a>()</dt><dd><tt>Use&nbsp;the&nbsp;old&nbsp;python-memcache&nbsp;server&nbsp;hash&nbsp;function.</tt></dd></dl>\n</td></tr></table><p>\n<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"section\">\n<tr bgcolor=\"#55aa55\">\n<td colspan=3 valign=bottom>&nbsp;<br>\n<font color=\"#ffffff\" face=\"helvetica, arial\"><big><strong>Data</strong></big></font></td></tr>\n    \n<tr><td bgcolor=\"#55aa55\"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>\n<td width=\"100%\"><strong>SERVER_MAX_KEY_LENGTH</strong> = 250<br>\n<strong>SERVER_MAX_VALUE_LENGTH</strong> = 1048576<br>\n<strong>__author__</strong> = 'Sean Reifschneider &lt;jafo-memcached@tummy.com&gt;'<br>\n<strong>__copyright__</strong> = 'Copyright (C) 2003 Danga Interactive'<br>\n<strong>__license__</strong> = 'Python Software Foundation License'<br>\n<strong>__version__</strong> = '1.48'</td></tr></table><p>\n<table width=\"100%\" cellspacing=0 cellpadding=2 border=0 summary=\"section\">\n<tr bgcolor=\"#7799ee\">\n<td colspan=3 valign=bottom>&nbsp;<br>\n<font color=\"#ffffff\" face=\"helvetica, arial\"><big><strong>Author</strong></big></font></td></tr>\n    \n<tr><td bgcolor=\"#7799ee\"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>\n<td width=\"100%\">Sean&nbsp;Reifschneider&nbsp;&lt;jafo-memcached@tummy.com&gt;</td></tr></table>\n</body></html>"
  },
  {
    "path": "docs/theme/nature/layout.html",
    "content": "{% extends \"basic/layout.html\" %}\n{% set script_files = script_files + [\"_static/sae.js\"] %}\n\n"
  },
  {
    "path": "docs/theme/nature/static/nature.css_t",
    "content": "/*\n * nature.css_t\n * ~~~~~~~~~~~~\n *\n * Sphinx stylesheet -- nature theme.\n *\n * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.\n * :license: BSD, see LICENSE for details.\n *\n */\n \n@import url(\"basic.css\");\n \n/* -- page layout ----------------------------------------------------------- */\n \nbody {\n    font-family: Arial, sans-serif;\n    font-size: 100%;\n    background-color: #111;\n    color: #555;\n    margin: 0;\n    padding: 0;\n}\n\ndiv.documentwrapper {\n    float: left;\n    width: 100%;\n}\n\ndiv.bodywrapper {\n    margin: 0 0 0 {{ theme_sidebarwidth|toint }}px;\n}\n\nhr {\n    border: 1px solid #B1B4B6;\n}\n \ndiv.document {\n    background-color: #eee;\n}\n \ndiv.body {\n    background-color: #ffffff;\n    color: #3E4349;\n    padding: 0 30px 30px 30px;\n    font-size: 0.9em;\n}\n \ndiv.footer {\n    color: #555;\n    width: 100%;\n    padding: 13px 0;\n    text-align: center;\n    font-size: 75%;\n}\n \ndiv.footer a {\n    color: #444;\n    text-decoration: underline;\n}\n \ndiv.related {\n    background-color: #6BA81E;\n    line-height: 32px;\n    color: #fff;\n    text-shadow: 0px 1px 0 #444;\n    font-size: 0.9em;\n}\n \ndiv.related a {\n    color: #E2F3CC;\n}\n \ndiv.sphinxsidebar {\n    font-size: 0.75em;\n    line-height: 1.5em;\n}\n\ndiv.sphinxsidebarwrapper{\n    padding: 20px 0;\n}\n \ndiv.sphinxsidebar h3,\ndiv.sphinxsidebar h4 {\n    font-family: Arial, sans-serif;\n    color: #222;\n    font-size: 1.2em;\n    font-weight: normal;\n    margin: 0;\n    padding: 5px 10px;\n    background-color: #ddd;\n    text-shadow: 1px 1px 0 white\n}\n\ndiv.sphinxsidebar h4{\n    font-size: 1.1em;\n}\n \ndiv.sphinxsidebar h3 a {\n    color: #444;\n}\n \n \ndiv.sphinxsidebar p {\n    color: #888;\n    padding: 5px 20px;\n}\n \ndiv.sphinxsidebar p.topless {\n}\n \ndiv.sphinxsidebar ul {\n    margin: 10px 20px;\n    padding: 0;\n    color: #000;\n}\n \ndiv.sphinxsidebar a {\n    color: #444;\n}\n \ndiv.sphinxsidebar input {\n    border: 1px solid #ccc;\n    font-family: sans-serif;\n    font-size: 1em;\n}\n\ndiv.sphinxsidebar input[type=text]{\n    margin-left: 20px;\n}\n \n/* -- body styles ----------------------------------------------------------- */\n \na {\n    color: #005B81;\n    text-decoration: none;\n}\n \na:hover {\n    color: #E32E00;\n    text-decoration: underline;\n}\n \ndiv.body h1,\ndiv.body h2,\ndiv.body h3,\ndiv.body h4,\ndiv.body h5,\ndiv.body h6 {\n    font-family: Arial, sans-serif;\n    background-color: #BED4EB;\n    font-weight: normal;\n    color: #212224;\n    margin: 30px 0px 10px 0px;\n    padding: 5px 0 5px 10px;\n    text-shadow: 0px 1px 0 white\n}\n \ndiv.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }\ndiv.body h2 { font-size: 150%; background-color: #C8D5E3; }\ndiv.body h3 { font-size: 120%; background-color: #D8DEE3; }\ndiv.body h4 { font-size: 110%; background-color: #D8DEE3; }\ndiv.body h5 { font-size: 100%; background-color: #D8DEE3; }\ndiv.body h6 { font-size: 100%; background-color: #D8DEE3; }\n \na.headerlink {\n    color: #c60f0f;\n    font-size: 0.8em;\n    padding: 0 4px 0 4px;\n    text-decoration: none;\n}\n \na.headerlink:hover {\n    background-color: #c60f0f;\n    color: white;\n}\n \ndiv.body p, div.body dd, div.body li {\n    line-height: 1.5em;\n}\n \ndiv.admonition p.admonition-title + p {\n    display: inline;\n}\n\ndiv.highlight{\n    background-color: white;\n}\n\ndiv.note {\n    background-color: #eee;\n    border: 1px solid #ccc;\n}\n \ndiv.seealso {\n    background-color: #ffc;\n    border: 1px solid #ff6;\n}\n \ndiv.topic {\n    background-color: #eee;\n}\n \ndiv.warning {\n    background-color: #ffe4e4;\n    border: 1px solid #f66;\n}\n \np.admonition-title {\n    display: inline;\n}\n \np.admonition-title:after {\n    content: \":\";\n}\n \npre {\n    padding: 10px;\n    background-color: White;\n    color: #222;\n    line-height: 1.2em;\n    border: 1px solid #C6C9CB;\n    font-size: 1.1em;\n    margin: 1.5em 0 1.5em 0;\n    -webkit-box-shadow: 1px 1px 1px #d8d8d8;\n    -moz-box-shadow: 1px 1px 1px #d8d8d8;\n}\n \ntt {\n    background-color: #ecf0f3;\n    color: #222;\n    /* padding: 1px 2px; */\n    font-size: 1.1em;\n    font-family: monospace;\n}\n\n.viewcode-back {\n    font-family: Arial, sans-serif;\n}\n\ndiv.viewcode-block:target {\n    background-color: #f4debf;\n    border-top: 1px solid #ac9;\n    border-bottom: 1px solid #ac9;\n}\n\n\ndiv.comment {\n    position:absolute;\n    left: -9999px;\n    top: -9999px;\n}\ndiv.comment a.email_link {\n    display: block;\n    width: 16px;\n    height: 16px;\n    cursor: pointer;\n    color: white;\n    text-decoration:none;\n    background: transparent url(./comment_white_yellow.gif) no-repeat -16px 0;\n}\n\ndiv.comment a.email_link:hover {\n    background-position:0 0;\n}\n\n\n\n"
  },
  {
    "path": "docs/theme/nature/static/sae.js",
    "content": "DESELEMENT = \"h2,h3,h4\"; //\"h1,h2,h3,h4,ul,div.section p,div.highlight-python\";\n\nfunction strip_tags(st) {\n    return st.replace(/<[^>]+>?[^<]*>/g, '');\n}\n\n$(document).ready(function() {\n    $(\"div.body > div.section\").find(DESELEMENT).each(function() {\n        if (!$(this).prev(\"div.comment\").length) {\n            var cmt = $('<div class=\"comment\"><a class=\"email_link\" title=\"点击提交Issue,反馈你的意见...\"></a></div>');\n            $(this).before(cmt);\n            cmt.offset({\n                //left: $(this).parents('.section').offset().left - 10,\n                left: $(this).offset().left - 10,\n                top: $(this).offset().top - 5\n            });\n        }\n    });\n\n    $(\"a.email_link\").hover(function() {\n        if ($(this).attr(\"href\") == null||$(this).attr(\"href\") == '') {\n            var body = $(this).parent(\"div.comment\").next().html();\n            \n            body = strip_tags(body).replace(/¶/g, '');\n            if (body.length > 100) {\n                body = body.substring(0, 100)+\"...\";\n            }\n\n            $(this).attr(\"href\"\n                , \"https://github.com/SAEPython/saepythondevguide/issues/new?title={文档}\" + encodeURIComponent(body));\n            $(this).attr(\"target\", \"_blank\");\n        }\n    }, function(){\n    });\n\n});\n"
  },
  {
    "path": "docs/theme/nature/theme.conf",
    "content": "[theme]\ninherit = basic\nstylesheet = nature.css\npygments_style = tango\n"
  },
  {
    "path": "docs/tools.rst",
    "content": "相关工具\n==============\n\n代码部署\n------------\n\nSAE Python使用SVN作为部署工具。使用SVN部署代码到SAE需要遵循以下规则：\n\n    SVN根目录下只允许存在以正整数命名的目录，不允许有文件存在，\n    这些目录为应用的版本目录，每个版本目录下才可以放应用对应版本的代码。\n\n以应用longtalk为例，这个应用有6个版本： ::\n  \n        jaime@westeros:~/longtalk$ ls\n        1  2  3  4  5  6\n        jaime@westeros:~/longtalk/1$ ls\n        index.wsgi myapp.py\n\nSVN限制： \n\n- 文件名或目录名不允许含有以下字符：\",*,?,<,>,|，另外文件或文件名的开始与结束也不允许有空格。\n- 上传单个文件大小不超过20M\n- 单个目录下的文 件个数不能超过2000个\n- 每个应用代码总大小不超过100M\n- 单个版本代码总大小不超过50M\n\n.. warning::\n   \n   1. 不要使用svn cp，mv，目前还不支持这两个操作。\n   2. 建议使用命令行工具中的saecloud deploy命令来部署代码。\n\n本地开发环境\n--------------\n\n本地开发环境仅为应用开发便利之用，对sae python环境的模拟并不完全。\n\n安装\n~~~~~~~~~\n\n直接使用 `pip` 或者 `easy_install` 安装 `sae-python-dev` 包即可。\n\n或者可以选择从github下载源码安装。\n\n::\n\n    $ git clone https://github.com/sinacloud/sae-python-dev-guide.git\n    $ cd sae-python-dev-guide/dev_server\n    $ python setup.py install\n\n基本使用\n~~~~~~~~~~\n\n进入应用的本地开发目录，也就是index.wsgi和config.yaml所在的目录。运行如下的命令启动测试server： ::\n\n    $ dev_server.py \n    MySQL config not found: app.py\n    Start development server on http://localhost:8080/\n\n访问 http://localhost:8080 端口就可以访问你的应用了。\n\n使用MySQL服务\n~~~~~~~~~~~~~~\n\n首先配置好MySQL本地开发server。然后使用 `--mysql` 参数运行dev_server.py。 ::\n\n    $ dev_server.py --mysql=user:password@host:port\n\n现在你可以在应用代码中像在SAE线上环境一样使用MySQL服务了。\ndev_server.py默认使用名为 `app_应用名` 的数据库。\n\n使用storage服务\n~~~~~~~~~~~~~~~~\n\n使用 `--storage-path` 参数运行dev_server.py。 ::\n\n    $ dev_server.py --storage-path=/path/to/local/storage/data\n\n本地的storage服务使用以下的目录结构来模拟线上的storage。 ::\n\n    storage-path/\n          domain1/\n                key1\n                key2\n          domain2/\n          domain3/\n\n--storage-path配置的路径下每个子文件夹会映射为storage中的一个domain，\n而每个子文件夹下的文件映射为domain下的一个key，其内容为对应key的数据。\n\n.. note::\n\n    为方便调试，dev_server自带的sae.storage在某个domain不存在的情况下会自动创建该domain。\n    线上环境中的domain需要在sae后台面板中手动创建。\n\n使用pylibmc\n~~~~~~~~~~~~~\n\ndev_server自带了一个dummy pylibmc，所以无须安装pylibmc就可以直接使用memcache服务了。\n该模块将所有的数据存贮在内存中，dev_server.py进程结束时，所有的数据都会丢失。\n\n使用kvdb\n~~~~~~~~~~~~~\n\nkvdb默认数据存在内存中，dev_server.py进程结束时，数据会全部丢失，如果需要保存数据，\n请使用如下命令行启动dev_server.py。 ::\n\n    $ dev_server.py --kvdb-file=/path/to/kvdb/local/file\n\n\n命令行工具saecloud\n--------------------\n\n部署代码\n~~~~~~~~~~\n\n进入应用目录（也就是config.yaml和index.wsgi所在的目录）。  ::\n\n    $ cat config.yaml \n    name: memorystone\n    version: 2\n    $ saecloud deploy\n\nsaecloud从config.yaml文件获得信息，判断将要把代码部署到哪个应用的哪个版本。上面的命令会将应用部署到memorystone的版本2上。\nsaecloud deploy命令接受一个可选参数: app代码所在路径，默认为当前目录'.'。\n\n.. note::\n\n   1. 删除应用版本目前仍然只能在前端管理界面中操作。\n   2. 如果代码量较大，则上传时间较慢，请耐心等待\n   3. 不推荐混合使用saecloud deploy和svn，虽然saecloud deploy部署之前会自动更新代码，但是如果有代码冲突则会导致本地状态不一致。解决办法为删除本地cache目录： `rm -rf ~/.saecloud`\n\n导出应用代码\n~~~~~~~~~~~~~~\n\n导出memorystone应用版本2到本地目录： ::\n\n    $ saecloud export memorystone 2 --username fooxxx@gmail.com --password barxxx\n    Exporting to memorystone\n\n第一个参数为应用名字，第二个参数为版本，可选，默认为版本1。 第一次使用时，请指定你的代码访问帐号信息：username 安全邮箱, password。之后的命令不用在输入此信息。\n\n.. note::\n\n   `deploy` 和 `export` 命令需要用到svn，请先安装svn命令行工具。\n   windows用户可以在这里下载：http://sourceforge.net/projects/win32svn/\n\n.. _howto-use-saecloud-install:\n\n安装依赖的第三方包\n~~~~~~~~~~~~~~~~~~\n\n在应用目录中执行下面的命令安装依赖的第三方包。 ::\n\n    saecloud install package [package ... ]\n\n如果应用的依赖关系比较多，也可以这些依赖关系写到依赖文件中，例如： ::\n\n    Framework==0.9.4\n    Library>=0.2\n\n假设上面的依赖文件的文件名为requirements.txt，你可以执行下面的命令安装所有的依赖包。 ::\n\n    saecloud install -r requirements.txt\n\n该命令会安装依赖包到应用目录下名为 `site-packages` 的目录里。如果文件比较多的话，推荐压缩site-packages目录。 ::\n\n    cd site-packages/\n    zip -r ../site-packages.zip .\n\n修改index.wsgi文件，在导入其它模块之前，将 `site-packages` 目录或者 `site-packages.zip` \n添加到module的搜索路径中。 ::\n\n    import os\n    import sys\n\n    root = os.path.dirname(__file__)\n\n    # 两者取其一\n    sys.path.insert(0, os.path.join(root, 'site-packages'))\n    sys.path.insert(0, os.path.join(root, 'site-packages.zip'))\n\n这样就可以在应用中使用这些依赖包了。\n\n.. tip::\n\n   安装指定版本的package：saecloud install package==version\n\n.. _cloudsql.py:\n\ncloudsql.py\n-------------\n\ncloudsql.py是SAE MySQL服务的一个命令行客户端，用户可以使用cloudsql.py来直接操作应用的线上数据库。 ::\n\n    alan@sina:~/python$ cloudsql.py -u ACCESSKEY -p SECRETKEY APP_NAME\n    SAE MySQL Client\n\n    Type \"help\" or \"?\" for help.\n\n    Connecting to Cloud SQL database \"app_shellpy\" on host w.rdc.sae.sina.com.cn.\n    Using readline for history management.\n    Loading history file \"/home/alan/.saecloud/app_shellpy.hist\"\n    mysql>\n\n如果想要在代码中直接操作线上的数据库，在 `import MySQLdb`  （并不一定要安装MySQLdb包）之前执行以下的代码即可： ::\n\n    # 只在本地开发环境中执行\n    import os\n    if 'SERVER_SOFTWARE' not in os.environ:\n        from sae._restful_mysql import monkey\n        monkey.patch()\n\n可用插件\n--------------\n\nSAE Python Shell\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nSAE Python Shell是一个wsgi中间件，提供了一个在线的interactive shell，便于在线调\n试app，查看系统信息等。（由 shellpy_ 修改而来)。\n\n.. _shellpy: http://code.google.com/p/google-app-engine-samples/source/browse/trunk/shell/shell.py\n\n\n..  py:class:: ShellMiddleware(app, password=None)\n    :module: sae.ext.shell\n\n    app: 你的应用callable\n\n    password: 可选，登录shell时需要输入的口令，用于保护shell不被非法访问。\n\n\n使用步骤:\n\n- 该插件需要使用 `memcache` 服务，请事先开启。\n\n- 修改index.wsgi，启用shell插件，示例如下::\n\n    import sae\n    from sae.ext.shell import ShellMiddleware\n\n    def app(environ, start_response):\n        status = '200 OK'\n        response_headers = [('Content-type', 'text/plain')]\n        start_response(status, response_headers)\n        return [\"Hello, world!\"]\n\n    application = sae.create_wsgi_app(ShellMiddleware(app))\n\n- 访问地址 https://<your-app-name>.sinaapp.com/_sae/shell ，根据提示输入你设置的口令\n\n- 或者你也可以在命令行下面使用 `saecloud shell <your-app-name> [-p PASSWORD]` 来访问在线shell。 ::\n\n    alan@sina:~/shellpy$ saecloud shell pylabs -proot\n    Python 2.7.3 (default, Mar 27 2013, 18:11:21) \n    [GCC 4.4.6 20120305 (Red Hat 4.4.6-4)]\n    Type \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n\n    >>> \n\n..  warning::\n\n    测试期间请谨慎使用，建议不使用时从源码中注释掉此shell。\n"
  },
  {
    "path": "examples/apibus/apibus_handler.py",
    "content": "#-*-coding: utf8 -*-\n\n\"\"\"\nSAE API auth handler for urllib2 and requests\n\nurllib2:\n\n>>> import urllib2\n>>> apibus_handler = SaeApibusAuthHandler(ACCESSKEY, SECRETKEY)\n>>> opener = urllib2.build_opener(apibus_handler)\n>>> print opener.open('http://g.sae.sina.com.cn/log/http/2015-06-18/1-access.log').read()\n\nrequests:\n\n>>> import requests\n>>> print requests.get('http://g.sae.sina.com.cn/log/http/2015-06-18/1-access.log?head/0/10|fields/ /1/2/3/4', auth=SaeApibusAuth(ACCESSKEY, SECRETKEY)).content\n\"\"\"\n\nimport hmac\nimport base64\nimport hashlib\nimport time\nimport urllib\nfrom urllib2 import BaseHandler, Request\n\n_APIBUS_URL_PREFIX = 'http://g.sae.sina.com.cn/'\n\nclass SaeApibusAuthHandler(BaseHandler):\n    # apibus handler must be in front\n    handler_order = 100\n\n    def __init__(self, accesskey, secretkey):\n        self.accesskey = accesskey\n        self.secretkey = secretkey\n\n    def http_request(self, req):\n        orig_url = req.get_full_url()\n        if not orig_url.startswith(_APIBUS_URL_PREFIX):\n            return req\n\n        timestamp = str(int(time.time()))\n        headers = [\n            ('x-sae-timestamp', timestamp),\n            ('x-sae-accesskey', self.accesskey),\n        ]\n        req.headers.update(headers)\n\n        method = req.get_method()\n        resource = urllib.unquote(req.get_full_url()[len(_APIBUS_URL_PREFIX)-1:])\n        sae_headers = [(k.lower(), v.lower()) for k, v in req.headers.items() if k.lower().startswith('x-sae-')]\n        req.add_header('Authorization', _signature(self.secretkey, method, resource, sae_headers))\n        return req\n\n    https_request = http_request\n\ntry:\n    from requests.auth import AuthBase\n\n    class SaeApibusAuth(AuthBase):\n        \"\"\"Attaches HTTP Basic Authentication to the given Request object.\"\"\"\n        def __init__(self, accesskey, secretkey):\n            self.accesskey = accesskey\n            self.secretkey = secretkey\n\n        def __call__(self, r):\n            timestamp = str(int(time.time()))\n            r.headers['x-sae-timestamp'] = timestamp\n            r.headers['x-sae-accesskey'] = self.accesskey\n            resource = urllib.unquote(r.url[len(_APIBUS_URL_PREFIX)-1:])\n            #resource = r.url[len(_APIBUS_URL_PREFIX)-1:]\n            sae_headers = [(k.lower(), v.lower()) for k, v in r.headers.items() if k.lower().startswith('x-sae-')]\n            r.headers['Authorization'] = _signature(self.secretkey, r.method, resource, sae_headers)\n            return r\nexcept ImportError:\n    # requests was not present!\n    pass\n\ndef _signature(secret, method, resource, headers):\n    msgToSign = \"\\n\".join([\n        method, resource,\n        \"\\n\".join([(k + \":\" + v) for k, v in sorted(headers)]),\n    ])\n    return \"SAEV1_HMAC_SHA256 \" + base64.b64encode(hmac.new(secret, msgToSign, hashlib.sha256).digest())\n\n"
  },
  {
    "path": "examples/bottle/README",
    "content": "Hello, Bottle!\n"
  },
  {
    "path": "examples/bottle/index.wsgi",
    "content": "from bottle import Bottle, run\n\nimport sae\n\napp = Bottle()\n\n@app.route('/')\ndef hello():\n    return \"Hello, world! - Bottle\"\n\napplication = sae.create_wsgi_app(app)\n\n"
  },
  {
    "path": "examples/django/1.2.7/README",
    "content": "Hello, Django!\n"
  },
  {
    "path": "examples/django/1.2.7/config.yaml",
    "content": "name: pylabs\nversion: 9\n"
  },
  {
    "path": "examples/django/1.2.7/index.wsgi",
    "content": "import sys\nimport os.path\n\n# manage.py is automatically created in each Django project. manage.py is a thin\n# wrapper around django-admin.py that takes care of two things for you before \n# delegating to django-admin.py:\n#\n#   It puts your project's package on sys.path.\n#   It sets the DJANGO_SETTINGS_MODULE environment variable so that it points to \n#   your project's settings.py file.\n#\n# ref: https://docs.djangoproject.com/en/1.4/ref/django-admin/\n\nos.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'\nsys.path.append(os.path.join(os.path.dirname(__file__), 'mysite'))\n\nimport sae\nimport django.core.handlers.wsgi\n\napplication = sae.create_wsgi_app(django.core.handlers.wsgi.WSGIHandler())\n"
  },
  {
    "path": "examples/django/1.2.7/mysite/__init__.py",
    "content": ""
  },
  {
    "path": "examples/django/1.2.7/mysite/demo/__init__.py",
    "content": ""
  },
  {
    "path": "examples/django/1.2.7/mysite/demo/models.py",
    "content": "from django.db import models\n\n# Create your models here.\nclass Demo(models.Model):\n    text = models.CharField(max_length=256)\n\n"
  },
  {
    "path": "examples/django/1.2.7/mysite/demo/tests.py",
    "content": "\"\"\"\nThis file demonstrates two different styles of tests (one doctest and one\nunittest). These will both pass when you run \"manage.py test\".\n\nReplace these with more appropriate tests for your application.\n\"\"\"\n\nfrom django.test import TestCase\n\nclass SimpleTest(TestCase):\n    def test_basic_addition(self):\n        \"\"\"\n        Tests that 1 + 1 always equals 2.\n        \"\"\"\n        self.failUnlessEqual(1 + 1, 2)\n\n__test__ = {\"doctest\": \"\"\"\nAnother way to test that 1 + 1 is equal to 2.\n\n>>> 1 + 1 == 2\nTrue\n\"\"\"}\n\n"
  },
  {
    "path": "examples/django/1.2.7/mysite/demo/views.py",
    "content": "# Create your views here.\n\nfrom django.http import HttpResponse\nfrom django.template import Template, Context\nfrom django.core.context_processors import csrf\nfrom demo.models import Demo\n\ndef showdemo(request):\n    if request.method == 'POST':\n        d = Demo(text=request.POST.get('text', ''))\n        d.save()\n\n    messages = Demo.objects.all()\n    t = Template(\"\"\"\n    {{ xxxx }}\n    {% for m in messages %}\n        <p>{{ m.text }}</p>\n    {% endfor %}\n    <form action=\"\" method=\"post\"> {% csrf_token %}\n        <div><textarea cols=\"40\" name=\"text\"></textarea></div>\n        <div><input type=\"submit\" /></div>\n    </form>\n    \"\"\");\n    d = {'messages': messages}\n    d.update(csrf(request))\n\n    return HttpResponse(t.render(Context(d)))\n\n"
  },
  {
    "path": "examples/django/1.2.7/mysite/manage.py",
    "content": "#!/usr/bin/env python\nfrom django.core.management import execute_manager\ntry:\n    import settings # Assumed to be in the same directory.\nexcept ImportError:\n    import sys\n    sys.stderr.write(\"Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\\nYou'll have to run django-admin.py, passing it your settings module.\\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\\n\" % __file__)\n    sys.exit(1)\n\nif __name__ == \"__main__\":\n    execute_manager(settings)\n"
  },
  {
    "path": "examples/django/1.2.7/mysite/settings.py",
    "content": "# Django settings for mysite project.\n\nDEBUG = True\nTEMPLATE_DEBUG = DEBUG\n\nADMINS = (\n    # ('Your Name', 'your_email@domain.com'),\n)\n\nMANAGERS = ADMINS\n\nfrom sae.const import (MYSQL_HOST, MYSQL_HOST_S,\n    MYSQL_PORT, MYSQL_USER, MYSQL_PASS, MYSQL_DB\n)\n\nDATABASES = {\n    'default': {\n        'ENGINE':   'mysql',\n        'NAME':     MYSQL_DB,\n        'USER':     MYSQL_USER, \n        'PASSWORD': MYSQL_PASS,\n        'HOST':     MYSQL_HOST,\n        'PORT':     MYSQL_PORT,\n    }\n}\n\n# Local time zone for this installation. Choices can be found here:\n# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name\n# although not all choices may be available on all operating systems.\n# On Unix systems, a value of None will cause Django to use the same\n# timezone as the operating system.\n# If running in a Windows environment this must be set to the same as your\n# system time zone.\nTIME_ZONE = 'America/Chicago'\n\n# Language code for this installation. All choices can be found here:\n# http://www.i18nguy.com/unicode/language-identifiers.html\nLANGUAGE_CODE = 'en-us'\n\nSITE_ID = 1\n\n# If you set this to False, Django will make some optimizations so as not\n# to load the internationalization machinery.\nUSE_I18N = True\n\n# If you set this to False, Django will not format dates, numbers and\n# calendars according to the current locale\nUSE_L10N = True\n\n# Absolute filesystem path to the directory that will hold user-uploaded files.\n# Example: \"/home/media/media.lawrence.com/\"\nMEDIA_ROOT = ''\n\n# URL that handles the media served from MEDIA_ROOT. Make sure to use a\n# trailing slash if there is a path component (optional in other cases).\n# Examples: \"http://media.lawrence.com\", \"http://example.com/media/\"\nMEDIA_URL = ''\n\n# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a\n# trailing slash.\n# Examples: \"http://foo.com/media/\", \"/media/\".\nADMIN_MEDIA_PREFIX = '/static/'\n\n# Make this unique, and don't share it with anybody.\nSECRET_KEY = 'xr45ymz8fs5wn*039+l462qwg7)7_yg$u7g6osv*3pynsr3#0#'\n\n# List of callables that know how to import templates from various sources.\nTEMPLATE_LOADERS = (\n    'django.template.loaders.filesystem.Loader',\n    'django.template.loaders.app_directories.Loader',\n#     'django.template.loaders.eggs.Loader',\n)\n\nMIDDLEWARE_CLASSES = (\n    'django.middleware.common.CommonMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n)\n\nROOT_URLCONF = 'mysite.urls'\n\nTEMPLATE_DIRS = (\n    # Put strings here, like \"/home/html/django_templates\" or \"C:/www/django/templates\".\n    # Always use forward slashes, even on Windows.\n    # Don't forget to use absolute paths, not relative paths.\n)\n\nINSTALLED_APPS = (\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.sites',\n    'django.contrib.messages',\n    'mysite.demo',\n    # Uncomment the next line to enable the admin:\n    'django.contrib.admin',\n    # Uncomment the next line to enable admin documentation:\n    # 'django.contrib.admindocs',\n)\n"
  },
  {
    "path": "examples/django/1.2.7/mysite/urls.py",
    "content": "from django.conf.urls.defaults import *\n\n# Uncomment the next two lines to enable the admin:\nfrom django.contrib import admin\nadmin.autodiscover()\n\nurlpatterns = patterns('',\n    # Example:\n    # (r'^mysite/', include('mysite.foo.urls')),\n    (r'^$', 'mysite.views.hello'),\n    (r'^demo/$', 'mysite.demo.views.showdemo'),\n\n    # Uncomment the admin/doc line below to enable admin documentation:\n    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),\n\n    # Uncomment the next line to enable the admin:\n    (r'^admin/', include(admin.site.urls)),\n)\n"
  },
  {
    "path": "examples/django/1.2.7/mysite/views.py",
    "content": "from django.http import HttpResponse\n\ndef hello(request):\n    return HttpResponse(\"Hello, world! - Django\")\n\n"
  },
  {
    "path": "examples/django/1.2.7/static/css/base.css",
    "content": "/*\n    DJANGO Admin styles\n*/\n\nbody {\n    margin: 0;\n    padding: 0;\n    font-size: 12px;\n    font-family: \"Lucida Grande\",\"DejaVu Sans\",\"Bitstream Vera Sans\",Verdana,Arial,sans-serif;\n    color: #333;\n    background: #fff;\n}\n\n/* LINKS */\n\na:link, a:visited {\n    color: #5b80b2;\n    text-decoration: none;\n}\n\na:hover {\n    color: #036;\n}\n\na img {\n    border: none;\n}\n\na.section:link, a.section:visited {\n    color: white;\n    text-decoration: none;\n}\n\n/* GLOBAL DEFAULTS */\n\np, ol, ul, dl {\n    margin: .2em 0 .8em 0;\n}\n\np {\n    padding: 0;\n    line-height: 140%;\n}\n\nh1,h2,h3,h4,h5 {\n    font-weight: bold;\n}\n\nh1 {\n    font-size: 18px;\n    color: #666;\n    padding: 0 6px 0 0;\n    margin: 0 0 .2em 0;\n}\n\nh2 {\n    font-size: 16px;\n    margin: 1em 0 .5em 0;\n}\n\nh2.subhead {\n    font-weight: normal;\n    margin-top: 0;\n}\n\nh3 {\n    font-size: 14px;\n    margin: .8em 0 .3em 0;\n    color: #666;\n    font-weight: bold;\n}\n\nh4 {\n    font-size: 12px;\n    margin: 1em 0 .8em 0;\n    padding-bottom: 3px;\n}\n\nh5 {\n    font-size: 10px;\n    margin: 1.5em 0 .5em 0;\n    color: #666;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n}\n\nul li {\n    list-style-type: square;\n    padding: 1px 0;\n}\n\nul.plainlist {\n    margin-left: 0 !important;\n}\n\nul.plainlist li {\n    list-style-type: none;\n}\n\nli ul {\n    margin-bottom: 0;\n}\n\nli, dt, dd {\n    font-size: 11px;\n    line-height: 14px;\n}\n\ndt {\n    font-weight: bold;\n    margin-top: 4px;\n}\n\ndd {\n    margin-left: 0;\n}\n\nform {\n    margin: 0;\n    padding: 0;\n}\n\nfieldset {\n    margin: 0;\n    padding: 0;\n}\n\nblockquote {\n    font-size: 11px;\n    color: #777;\n    margin-left: 2px;\n    padding-left: 10px;\n    border-left: 5px solid #ddd;\n}\n\ncode, pre {\n    font-family: \"Bitstream Vera Sans Mono\", Monaco, \"Courier New\", Courier, monospace;\n    background: inherit;\n    color: #666;\n    font-size: 11px;\n}\n\npre.literal-block {\n    margin: 10px;\n    background: #eee;\n    padding: 6px 8px;\n}\n\ncode strong {\n    color: #930;\n}\n\nhr {\n    clear: both;\n    color: #eee;\n    background-color: #eee;\n    height: 1px;\n    border: none;\n    margin: 0;\n    padding: 0;\n    font-size: 1px;\n    line-height: 1px;\n}\n\n/* TEXT STYLES & MODIFIERS */\n\n.small {\n    font-size: 11px;\n}\n\n.tiny {\n    font-size: 10px;\n}\n\np.tiny {\n    margin-top: -2px;\n}\n\n.mini {\n    font-size: 9px;\n}\n\np.mini {\n    margin-top: -3px;\n}\n\n.help, p.help {\n    font-size: 10px !important;\n    color: #999;\n}\n\np img, h1 img, h2 img, h3 img, h4 img, td img {\n    vertical-align: middle;\n}\n\n.quiet, a.quiet:link, a.quiet:visited {\n    color: #999 !important;\n    font-weight: normal !important;\n}\n\n.quiet strong {\n    font-weight: bold !important;\n}\n\n.float-right {\n    float: right;\n}\n\n.float-left {\n    float: left;\n}\n\n.clear {\n    clear: both;\n}\n\n.align-left {\n    text-align: left;\n}\n\n.align-right {\n    text-align: right;\n}\n\n.example {\n    margin: 10px 0;\n    padding: 5px 10px;\n    background: #efefef;\n}\n\n.nowrap {\n    white-space: nowrap;\n}\n\n/* TABLES */\n\ntable {\n    border-collapse: collapse;\n    border-color: #ccc;\n}\n\ntd, th {\n    font-size: 11px;\n    line-height: 13px;\n    border-bottom: 1px solid #eee;\n    vertical-align: top;\n    padding: 5px;\n    font-family: \"Lucida Grande\", Verdana, Arial, sans-serif;\n}\n\nth {\n    text-align: left;\n    font-size: 12px;\n    font-weight: bold;\n}\n\nthead th,\ntfoot td {\n    color: #666;\n    padding: 2px 5px;\n    font-size: 11px;\n    background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;\n    border-left: 1px solid #ddd;\n    border-bottom: 1px solid #ddd;\n}\n\ntfoot td {\n    border-bottom: none;\n    border-top: 1px solid #ddd;\n}\n\nthead th:first-child,\ntfoot td:first-child {\n    border-left: none !important;\n}\n\nthead th.optional {\n    font-weight: normal !important;\n}\n\nfieldset table {\n    border-right: 1px solid #eee;\n}\n\ntr.row-label td {\n    font-size: 9px;\n    padding-top: 2px;\n    padding-bottom: 0;\n    border-bottom: none;\n    color: #666;\n    margin-top: -1px;\n}\n\ntr.alt {\n    background: #f6f6f6;\n}\n\n.row1 {\n    background: #EDF3FE;\n}\n\n.row2 {\n    background: white;\n}\n\n/* SORTABLE TABLES */\n\nthead th a:link, thead th a:visited {\n    color: #666;\n    display: block;\n}\n\ntable thead th.sorted {\n    background-position: bottom left !important;\n}\n\ntable thead th.sorted a {\n    padding-right: 13px;\n}\n\ntable thead th.ascending a {\n    background: url(../img/admin/arrow-up.gif) right .4em no-repeat;\n}\n\ntable thead th.descending a {\n    background: url(../img/admin/arrow-down.gif) right .4em no-repeat;\n}\n\n/* ORDERABLE TABLES */\n\ntable.orderable tbody tr td:hover {\n    cursor: move;\n}\n\ntable.orderable tbody tr td:first-child {\n    padding-left: 14px;\n    background-image: url(../img/admin/nav-bg-grabber.gif);\n    background-repeat: repeat-y;\n}\n\ntable.orderable-initalized .order-cell, body>tr>td.order-cell {\n    display: none;\n}\n\n/* FORM DEFAULTS */\n\ninput, textarea, select, .form-row p {\n    margin: 2px 0;\n    padding: 2px 3px;\n    vertical-align: middle;\n    font-family: \"Lucida Grande\", Verdana, Arial, sans-serif;\n    font-weight: normal;\n    font-size: 11px;\n}\n\ntextarea {\n    vertical-align: top !important;\n}\n\ninput[type=text], input[type=password], textarea, select, .vTextField {\n    border: 1px solid #ccc;\n}\n\n/* FORM BUTTONS */\n\n.button, input[type=submit], input[type=button], .submit-row input {\n    background: white url(../img/admin/nav-bg.gif) bottom repeat-x;\n    padding: 3px 5px;\n    color: black;\n    border: 1px solid #bbb;\n    border-color: #ddd #aaa #aaa #ddd;\n}\n\n.button:active, input[type=submit]:active, input[type=button]:active {\n    background-image: url(../img/admin/nav-bg-reverse.gif);\n    background-position: top;\n}\n\n.button.default, input[type=submit].default, .submit-row input.default {\n    border: 2px solid #5b80b2;\n    background: #7CA0C7 url(../img/admin/default-bg.gif) bottom repeat-x;\n    font-weight: bold;\n    color: white;\n    float: right;\n}\n\n.button.default:active, input[type=submit].default:active {\n    background-image: url(../img/admin/default-bg-reverse.gif);\n    background-position: top;\n}\n\n/* MODULES */\n\n.module {\n    border: 1px solid #ccc;\n    margin-bottom: 5px;\n    background: white;\n}\n\n.module p, .module ul, .module h3, .module h4, .module dl, .module pre {\n    padding-left: 10px;\n    padding-right: 10px;\n}\n\n.module blockquote {\n    margin-left: 12px;\n}\n\n.module ul, .module ol {\n    margin-left: 1.5em;\n}\n\n.module h3 {\n    margin-top: .6em;\n}\n\n.module h2, .module caption, .inline-group h2 {\n    margin: 0;\n    padding: 2px 5px 3px 5px;\n    font-size: 11px;\n    text-align: left;\n    font-weight: bold;\n    background: #7CA0C7 url(../img/admin/default-bg.gif) top left repeat-x;\n    color: white;\n}\n\n.module table {\n    border-collapse: collapse;\n}\n\n/* MESSAGES & ERRORS */\n\nul.messagelist {\n    padding: 0 0 5px 0;\n    margin: 0;\n}\n\nul.messagelist li {\n    font-size: 12px;\n    display: block;\n    padding: 4px 5px 4px 25px;\n    margin: 0 0 3px 0;\n    border-bottom: 1px solid #ddd;\n    color: #666;\n    background: #ffc url(../img/admin/icon_success.gif) 5px .3em no-repeat;\n}\n\nul.messagelist li.warning{\n    background-image: url(../img/admin/icon_alert.gif);\n}\n\nul.messagelist li.error{\n    background-image: url(../img/admin/icon_error.gif);\n}\n\n.errornote {\n    font-size: 12px !important;\n    display: block;\n    padding: 4px 5px 4px 25px;\n    margin: 0 0 3px 0;\n    border: 1px solid red;\n    color: red;\n    background: #ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat;\n}\n\nul.errorlist {\n    margin: 0 !important;\n    padding: 0 !important;\n}\n\n.errorlist li {\n    font-size: 12px !important;\n    display: block;\n    padding: 4px 5px 4px 25px;\n    margin: 0 0 3px 0;\n    border: 1px solid red;\n    color: white;\n    background: red url(../img/admin/icon_alert.gif) 5px .3em no-repeat;\n}\n\n.errorlist li a {\n \tcolor: white;\n    text-decoration: underline;\n}\n\ntd ul.errorlist {\n    margin: 0 !important;\n    padding: 0 !important;\n}\n\ntd ul.errorlist li {\n    margin: 0 !important;\n}\n\n.errors {\n    background: #ffc;\n}\n\n.errors input, .errors select, .errors textarea {\n    border: 1px solid red;\n}\n\ndiv.system-message {\n    background: #ffc;\n    margin: 10px;\n    padding: 6px 8px;\n    font-size: .8em;\n}\n\ndiv.system-message p.system-message-title {\n    padding: 4px 5px 4px 25px;\n    margin: 0;\n    color: red;\n    background: #ffc url(../img/admin/icon_error.gif) 5px .3em no-repeat;\n}\n\n.description {\n    font-size: 12px;\n    padding: 5px 0 0 12px;\n}\n\n/* BREADCRUMBS */\n\ndiv.breadcrumbs {\n    background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;\n    padding: 2px 8px 3px 8px;\n    font-size: 11px;\n    color: #999;\n    border-top: 1px solid white;\n    border-bottom: 1px solid #ccc;\n    text-align: left;\n}\n\n/* ACTION ICONS */\n\n.addlink {\n    padding-left: 12px;\n    background: url(../img/admin/icon_addlink.gif) 0 .2em no-repeat;\n}\n\n.changelink {\n    padding-left: 12px;\n    background: url(../img/admin/icon_changelink.gif) 0 .2em no-repeat;\n}\n\n.deletelink {\n    padding-left: 12px;\n    background: url(../img/admin/icon_deletelink.gif) 0 .25em no-repeat;\n}\n\na.deletelink:link, a.deletelink:visited {\n    color: #CC3434;\n}\n\na.deletelink:hover {\n    color: #993333;\n}\n\n/* OBJECT TOOLS */\n\n.object-tools {\n    font-size: 10px;\n    font-weight: bold;\n    font-family: Arial,Helvetica,sans-serif;\n    padding-left: 0;\n    float: right;\n    position: relative;\n    margin-top: -2.4em;\n    margin-bottom: -2em;\n}\n\n.form-row .object-tools {\n    margin-top: 5px;\n    margin-bottom: 5px;\n    float: none;\n    height: 2em;\n    padding-left: 3.5em;\n}\n\n.object-tools li {\n    display: block;\n    float: left;\n    background: url(../img/admin/tool-left.gif) 0 0 no-repeat;\n    padding: 0 0 0 8px;\n    margin-left: 2px;\n    height: 16px;\n}\n\n.object-tools li:hover {\n    background: url(../img/admin/tool-left_over.gif) 0 0 no-repeat;\n}\n\n.object-tools a:link, .object-tools a:visited {\n    display: block;\n    float: left;\n    color: white;\n    padding: .1em 14px .1em 8px;\n    height: 14px;\n    background: #999 url(../img/admin/tool-right.gif) 100% 0 no-repeat;\n}\n\n.object-tools a:hover, .object-tools li:hover a {\n    background: #5b80b2 url(../img/admin/tool-right_over.gif) 100% 0 no-repeat;\n}\n\n.object-tools a.viewsitelink, .object-tools a.golink {\n    background: #999 url(../img/admin/tooltag-arrowright.gif) top right no-repeat;\n    padding-right: 28px;\n}\n\n.object-tools a.viewsitelink:hover, .object-tools a.golink:hover {\n    background: #5b80b2 url(../img/admin/tooltag-arrowright_over.gif) top right no-repeat;\n}\n\n.object-tools a.addlink {\n    background: #999 url(../img/admin/tooltag-add.gif) top right no-repeat;\n    padding-right: 28px;\n}\n\n.object-tools a.addlink:hover {\n    background: #5b80b2 url(../img/admin/tooltag-add_over.gif) top right no-repeat;\n}\n\n/* OBJECT HISTORY */\n\ntable#change-history {\n    width: 100%;\n}\n\ntable#change-history tbody th {\n    width: 16em;\n}\n\n/* PAGE STRUCTURE */\n\n#container {\n    position: relative;\n    width: 100%;\n    min-width: 760px;\n    padding: 0;\n}\n\n#content {\n    margin: 10px 15px;\n}\n\n#header {\n    width: 100%;\n}\n\n#content-main {\n    float: left;\n    width: 100%;\n}\n\n#content-related {\n    float: right;\n    width: 18em;\n    position: relative;\n    margin-right: -19em;\n}\n\n#footer {\n    clear: both;\n    padding: 10px;\n}\n\n/* COLUMN TYPES */\n\n.colMS {\n    margin-right: 20em !important;\n}\n\n.colSM {\n    margin-left: 20em !important;\n}\n\n.colSM #content-related {\n    float: left;\n    margin-right: 0;\n    margin-left: -19em;\n}\n\n.colSM #content-main {\n    float: right;\n}\n\n.popup .colM {\n    width: 95%;\n}\n\n.subcol {\n    float: left;\n    width: 46%;\n    margin-right: 15px;\n}\n\n.dashboard #content {\n    width: 500px;\n}\n\n/* HEADER */\n\n#header {\n    background: #417690;\n    color: #ffc;\n    overflow: hidden;\n}\n\n#header a:link, #header a:visited {\n    color: white;\n}\n\n#header a:hover {\n    text-decoration: underline;\n}\n\n#branding h1 {\n    padding: 0 10px;\n    font-size: 18px;\n    margin: 8px 0;\n    font-weight: normal;\n    color: #f4f379;\n}\n\n#branding h2 {\n    padding: 0 10px;\n    font-size: 14px;\n    margin: -8px 0 8px 0;\n    font-weight: normal;\n    color: #ffc;\n}\n\n#user-tools {\n    position: absolute;\n    top: 0;\n    right: 0;\n    padding: 1.2em 10px;\n    font-size: 11px;\n    text-align: right;\n}\n\n/* SIDEBAR */\n\n#content-related h3 {\n    font-size: 12px;\n    color: #666;\n    margin-bottom: 3px;\n}\n\n#content-related h4 {\n    font-size: 11px;\n}\n\n#content-related .module h2 {\n    background: #eee url(../img/admin/nav-bg.gif) bottom left repeat-x;\n    color: #666;\n}\n\n"
  },
  {
    "path": "examples/django/1.2.7/static/css/changelists.css",
    "content": "/* CHANGELISTS */\n\n#changelist {\n    position: relative;\n    width: 100%;\n}\n\n#changelist table {\n    width: 100%;\n}\n\n.change-list .hiddenfields { display:none; }\n\n.change-list .filtered table {\n    border-right: 1px solid #ddd;\n}\n\n.change-list .filtered {\n    min-height: 400px;\n}\n\n.change-list .filtered {\n    background: white url(../img/admin/changelist-bg.gif) top right repeat-y !important;\n}\n\n.change-list .filtered table, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull {\n    margin-right: 160px !important;\n    width: auto !important;\n}\n\n.change-list .filtered table tbody th {\n    padding-right: 1em;\n}\n\n#changelist .toplinks {\n    border-bottom: 1px solid #ccc !important;\n}\n\n#changelist .paginator {\n    color: #666;\n    border-top: 1px solid #eee;\n    border-bottom: 1px solid #eee;\n    background: white url(../img/admin/nav-bg.gif) 0 180% repeat-x;\n    overflow: hidden;\n}\n\n.change-list .filtered .paginator {\n    border-right: 1px solid #ddd;\n}\n\n/* CHANGELIST TABLES */\n\n#changelist table thead th {\n    white-space: nowrap;\n    vertical-align: middle;\n}\n\n#changelist table thead th.action-checkbox-column {\n    width: 1.5em;\n    text-align: center;\n}\n\n#changelist table tbody td, #changelist table tbody th {\n    border-left: 1px solid #ddd;\n}\n\n#changelist table tbody td:first-child, #changelist table tbody th:first-child {\n    border-left: 0;\n    border-right: 1px solid #ddd;\n}\n\n#changelist table tbody td.action-checkbox {\n    text-align:center;\n}\n\n#changelist table tfoot {\n    color: #666;\n}\n\n/* TOOLBAR */\n\n#changelist #toolbar {\n    padding: 3px;\n    border-bottom: 1px solid #ddd;\n    background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;\n    color: #666;\n}\n\n#changelist #toolbar form input {\n    font-size: 11px;\n    padding: 1px 2px;\n}\n\n#changelist #toolbar form #searchbar {\n    padding: 2px;\n}\n\n#changelist #changelist-search img {\n    vertical-align: middle;\n}\n\n/* FILTER COLUMN */\n\n#changelist-filter {\n    position: absolute;\n    top: 0;\n    right: 0;\n    z-index: 1000;\n    width: 160px;\n    border-left: 1px solid #ddd;\n    background: #efefef;\n    margin: 0;\n}\n\n#changelist-filter h2 {\n    font-size: 11px;\n    padding: 2px 5px;\n    border-bottom: 1px solid #ddd;\n}\n\n#changelist-filter h3 {\n    font-size: 12px;\n    margin-bottom: 0;\n}\n\n#changelist-filter ul {\n    padding-left: 0;\n    margin-left: 10px;\n}\n\n#changelist-filter li {\n    list-style-type: none;\n    margin-left: 0;\n    padding-left: 0;\n}\n\n#changelist-filter a {\n    color: #999;\n}\n\n#changelist-filter a:hover {\n    color: #036;\n}\n\n#changelist-filter li.selected {\n    border-left: 5px solid #ccc;\n    padding-left: 5px;\n    margin-left: -10px;\n}\n\n#changelist-filter li.selected a {\n    color: #5b80b2 !important;\n}\n\n/* DATE DRILLDOWN */\n\n.change-list ul.toplinks {\n    display: block;\n    background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;\n    border-top: 1px solid white;\n    float: left;\n    padding: 0 !important;\n    margin: 0 !important;\n    width: 100%;\n}\n\n.change-list ul.toplinks li {\n    float: left;\n    width: 9em;\n    padding: 3px 6px;\n    font-weight: bold;\n    list-style-type: none;\n}\n\n.change-list ul.toplinks .date-back a {\n    color: #999;\n}\n\n.change-list ul.toplinks .date-back a:hover {\n    color: #036;\n}\n\n/* PAGINATOR */\n\n.paginator {\n    font-size: 11px;\n    padding-top: 10px;\n    padding-bottom: 10px;\n    line-height: 22px;\n    margin: 0;\n    border-top: 1px solid #ddd;\n}\n\n.paginator a:link, .paginator a:visited {\n    padding: 2px 6px;\n    border: solid 1px #ccc;\n    background: white;\n    text-decoration: none;\n}\n\n.paginator a.showall {\n    padding: 0 !important;\n    border: none !important;\n}\n\n.paginator a.showall:hover {\n    color: #036 !important;\n    background: transparent !important;\n}\n\n.paginator .end {\n    border-width: 2px !important;\n    margin-right: 6px;\n}\n\n.paginator .this-page {\n    padding: 2px 6px;\n    font-weight: bold;\n    font-size: 13px;\n    vertical-align: top;\n}\n\n.paginator a:hover {\n    color: white;\n    background: #5b80b2;\n    border-color: #036;\n}\n\n/* ACTIONS */\n\n.filtered .actions {\n    margin-right: 160px !important;\n    border-right: 1px solid #ddd;\n}\n\n#changelist table input {\n    margin: 0;\n}\n\n#changelist table tbody tr.selected {\n    background-color: #FFFFCC;\n}\n\n#changelist .actions {\n    color: #999;\n    padding: 3px;\n    border-top: 1px solid #fff;\n    border-bottom: 1px solid #ddd;\n    background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;\n}\n\n#changelist .actions.selected {\n    background: #fffccf;\n    border-top: 1px solid #fffee8;\n    border-bottom: 1px solid #edecd6;\n}\n\n#changelist .actions span.all,\n#changelist .actions span.action-counter,\n#changelist .actions span.clear,\n#changelist .actions span.question {\n    font-size: 11px;\n    margin: 0 0.5em;\n    display: none;\n}\n\n#changelist .actions:last-child {\n    border-bottom: none;\n}\n\n#changelist .actions select {\n    border: 1px solid #aaa;\n    margin-left: 0.5em;\n    padding: 1px 2px;\n}\n\n#changelist .actions label {\n    font-size: 11px;\n    margin-left: 0.5em;\n}\n\n#changelist #action-toggle {\n    display: none;\n}\n\n#changelist .actions .button {\n    font-size: 11px;\n    padding: 1px 2px;\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/css/dashboard.css",
    "content": "/* DASHBOARD */\n\n.dashboard .module table th {\n    width: 100%;\n}\n\n.dashboard .module table td {\n    white-space: nowrap;\n}\n\n.dashboard .module table td a {\n    display: block;\n    padding-right: .6em;\n}\n\n/* RECENT ACTIONS MODULE */\n\n.module ul.actionlist {\n    margin-left: 0;\n}\n\nul.actionlist li {\n    list-style-type: none;\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/css/forms.css",
    "content": "@import url('widgets.css');\n\n/* FORM ROWS */\n\n.form-row {\n    overflow: hidden;\n    padding: 8px 12px;\n    font-size: 11px;\n    border-bottom: 1px solid #eee;\n}\n\n.form-row img, .form-row input {\n    vertical-align: middle;\n}\n\nform .form-row p {\n    padding-left: 0;\n    font-size: 11px;\n}\n\n/* FORM LABELS */\n\nform h4 {\n    margin: 0 !important;\n    padding: 0 !important;\n    border: none !important;\n}\n\nlabel {\n    font-weight: normal !important;\n    color: #666;\n    font-size: 12px;\n}\n\n.required label, label.required {\n    font-weight: bold !important;\n    color: #333 !important;\n}\n\n/* RADIO BUTTONS */\n\nform ul.radiolist li {\n    list-style-type: none;\n}\n\nform ul.radiolist label {\n    float: none;\n    display: inline;\n}\n\nform ul.inline {\n    margin-left: 0;\n    padding: 0;\n}\n\nform ul.inline li {\n    float: left;\n    padding-right: 7px;\n}\n\n/* ALIGNED FIELDSETS */\n\n.aligned label {\n    display: block;\n    padding: 3px 10px 0 0;\n    float: left;\n    width: 8em;\n}\n\n.aligned ul label {\n    display: inline;\n    float: none;\n    width: auto;\n}\n\n.colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField {\n    width: 350px;\n}\n\nform .aligned p, form .aligned ul {\n    margin-left: 7em;\n    padding-left: 30px;\n}\n\nform .aligned table p {\n    margin-left: 0;\n    padding-left: 0;\n}\n\nform .aligned p.help {\n    padding-left: 38px;\n}\n\n.aligned .vCheckboxLabel {\n    float: none !important;\n    display: inline;\n    padding-left: 4px;\n}\n\n.colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField {\n    width: 610px;\n}\n\n.checkbox-row p.help {\n    margin-left: 0;\n    padding-left: 0 !important;\n}\n\nfieldset .field-box {\n    float: left;\n    margin-right: 20px;\n}\n\n/* WIDE FIELDSETS */\n\n.wide label {\n    width: 15em !important;\n}\n\nform .wide p {\n    margin-left: 15em;\n}\n\nform .wide p.help {\n    padding-left: 38px;\n}\n\n.colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField {\n    width: 450px;\n}\n\n/* COLLAPSED FIELDSETS */\n\nfieldset.collapsed * {\n    display: none;\n}\n\nfieldset.collapsed h2, fieldset.collapsed {\n    display: block !important;\n}\n\nfieldset.collapsed h2 {\n    background-image: url(../img/admin/nav-bg.gif);\n    background-position: bottom left;\n    color: #999;\n}\n\nfieldset.collapsed .collapse-toggle {\n    background: transparent;\n    display: inline !important;\n}\n\n/* MONOSPACE TEXTAREAS */\n\nfieldset.monospace textarea {\n    font-family: \"Bitstream Vera Sans Mono\",Monaco,\"Courier New\",Courier,monospace;\n}\n\n/* SUBMIT ROW */\n\n.submit-row {\n    padding: 5px 7px;\n    text-align: right;\n    background: white url(../img/admin/nav-bg.gif) 0 100% repeat-x;\n    border: 1px solid #ccc;\n    margin: 5px 0;\n    overflow: hidden;\n}\n\n.submit-row input {\n    margin: 0 0 0 5px;\n}\n\n.submit-row p {\n    margin: 0.3em;\n}\n\n.submit-row p.deletelink-box {\n    float: left;\n}\n\n.submit-row .deletelink {\n    background: url(../img/admin/icon_deletelink.gif) 0 50% no-repeat;\n    padding-left: 14px;\n}\n\n/* CUSTOM FORM FIELDS */\n\n.vSelectMultipleField {\n    vertical-align: top !important;\n}\n\n.vCheckboxField {\n    border: none;\n}\n\n.vDateField, .vTimeField {\n    margin-right: 2px;\n}\n\n.vURLField {\n    width: 30em;\n}\n\n.vLargeTextField, .vXMLLargeTextField {\n    width: 48em;\n}\n\n.flatpages-flatpage #id_content {\n    height: 40.2em;\n}\n\n.module table .vPositiveSmallIntegerField {\n    width: 2.2em;\n}\n\n.vTextField {\n    width: 20em;\n}\n\n.vIntegerField {\n    width: 5em;\n}\n\n.vForeignKeyRawIdAdminField {\n    width: 5em;\n}\n\n/* INLINES */\n\n.inline-group {\n    padding: 0;\n    border: 1px solid #ccc;\n    margin: 10px 0;\n}\n\n.inline-group .aligned label {\n    width: 8em;\n}\n\n.inline-related {\n    position: relative;\n}\n\n.inline-related h3 {\n    margin: 0;\n    color: #666;\n    padding: 3px 5px;\n    font-size: 11px;\n    background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;\n    border-bottom: 1px solid #ddd;\n}\n\n.inline-related h3 span.delete {\n    float: right;\n}\n\n.inline-related h3 span.delete label {\n    margin-left: 2px;\n    font-size: 11px;\n}\n\n.inline-related fieldset {\n    margin: 0;\n    background: #fff;\n    border: none;\n}\n\n.inline-related fieldset.module h3 {\n    margin: 0;\n    padding: 2px 5px 3px 5px;\n    font-size: 11px;\n    text-align: left;\n    font-weight: bold;\n    background: #bcd;\n    color: #fff;\n}\n\n.inline-group .tabular fieldset.module {\n    border: none;\n    border-bottom: 1px solid #ddd;\n}\n\n.inline-related.tabular fieldset.module table {\n    width: 100%;\n}\n\n.last-related fieldset {\n    border: none;\n}\n\n.inline-group .tabular tr.has_original td {\n    padding-top: 2em;\n}\n\n.inline-group .tabular tr td.original {\n    padding: 2px 0 0 0;\n    width: 0;\n    _position: relative;\n}\n\n.inline-group .tabular th.original {\n    width: 0px;\n    padding: 0;\n}\n\n.inline-group .tabular td.original p {\n    position: absolute;\n    left: 0;\n    height: 1.1em;\n    padding: 2px 7px;\n    overflow: hidden;\n    font-size: 9px;\n    font-weight: bold;\n    color: #666;\n    _width: 700px;\n}\n\n.inline-group ul.tools {\n    padding: 0;\n    margin: 0;\n    list-style: none;\n}\n\n.inline-group ul.tools li {\n    display: inline;\n    padding: 0 5px;\n}\n\n.inline-group div.add-row,\n.inline-group .tabular tr.add-row td {\n    color: #666;\n    padding: 3px 5px;\n    border-bottom: 1px solid #ddd;\n    background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;\n}\n\n.inline-group .tabular tr.add-row td {\n    padding: 4px 5px 3px;\n    border-bottom: none;\n}\n\n.inline-group ul.tools a.add,\n.inline-group div.add-row a,\n.inline-group .tabular tr.add-row td a {\n    background: url(../img/admin/icon_addlink.gif) 0 50% no-repeat;\n    padding-left: 14px;\n    font-size: 11px;\n    outline: 0; /* Remove dotted border around link */\n}\n\n.empty-form {\n    display: none;\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/css/ie.css",
    "content": "/* IE 6 & 7 */\n\n/* Proper fixed width for dashboard in IE6 */\n\n.dashboard #content {\n    *width: 768px;\n}\n\n.dashboard #content-main {\n    *width: 535px;\n}\n\n/* IE 6 ONLY */\n\n/* Keep header from flowing off the page */\n\n#container {\n    _position: static;\n}\n\n/* Put the right sidebars back on the page */\n\n.colMS #content-related {\n    _margin-right: 0;\n    _margin-left: 10px;\n    _position: static;\n}\n\n/* Put the left sidebars back on the page */\n\n.colSM #content-related {\n    _margin-right: 10px;\n    _margin-left: -115px;\n    _position: static;\n}\n\n.form-row {\n    _height: 1%;\n}\n\n/* Fix right margin for changelist filters in IE6 */\n\n#changelist-filter ul {\n    _margin-right: -10px;\n}\n\n/* IE ignores min-height, but treats height as if it were min-height */\n\n.change-list .filtered {\n    _height: 400px;\n}\n\n/* IE doesn't know alpha transparency in PNGs */\n\n.inline-deletelink {\n    background: transparent url(../img/admin/inline-delete-8bit.png) no-repeat;\n}"
  },
  {
    "path": "examples/django/1.2.7/static/css/login.css",
    "content": "/* LOGIN FORM */\n\nbody.login {\n    background: #eee;\n}\n\n.login #container {\n    background: white;\n    border: 1px solid #ccc;\n    width: 28em;\n    min-width: 300px;\n    margin-left: auto;\n    margin-right: auto;\n    margin-top: 100px;\n}\n\n.login #content-main {\n    width: 100%;\n}\n\n.login form {\n    margin-top: 1em;\n}\n\n.login .form-row {\n    padding: 4px 0;\n    float: left;\n    width: 100%;\n}\n\n.login .form-row label {\n    float: left;\n    width: 9em;\n    padding-right: 0.5em;\n    line-height: 2em;\n    text-align: right;\n    font-size: 1em;\n    color: #333;\n}\n\n.login .form-row #id_username, .login .form-row #id_password {\n    width: 14em;\n}\n\n.login span.help {\n    font-size: 10px;\n    display: block;\n}\n\n.login .submit-row {\n    clear: both;\n    padding: 1em 0 0 9.4em;\n}\n\n"
  },
  {
    "path": "examples/django/1.2.7/static/css/rtl.css",
    "content": "body {\n    direction: rtl;\n}\n\n/* LOGIN */\n\n.login .form-row {\n    float: right;\n}\n\n.login .form-row label {\n    float: right;\n    padding-left: 0.5em;\n    padding-right: 0;\n    text-align: left;\n}\n\n.login .submit-row {\n    clear: both;\n    padding: 1em 9.4em 0 0;\n}\n\n/* GLOBAL */\n\nth {\n    text-align: right;\n}\n\n.module h2, .module caption {\n    text-align: right;\n}\n\n.addlink, .changelink {\n    padding-left: 0px;\n    padding-right: 12px;\n    background-position: 100% 0.2em;\n}\n\n.deletelink {\n    padding-left: 0px;\n    padding-right: 12px;\n    background-position: 100% 0.25em;\n}\n\n.object-tools {\n    float: left;\n}\n\n/* LAYOUT */\n\n#user-tools {\n    right: auto;\n    left: 0;\n    text-align: left;\n}\n\ndiv.breadcrumbs {\n    text-align: right;\n}\n\n#content-main {\n    float: right;\n}\n\n#content-related {\n    float: left;\n    margin-left: -19em;\n    margin-right: auto;\n}\n\n.colMS {\n    margin-left: 20em !important;\n    margin-right: 10px !important;\n}\n\n/* dashboard styles */\n\n.dashboard .module table td a {\n    padding-left: .6em;\n    padding-right: 12px;\n}\n\n/* changelists styles */\n\n.change-list ul.toplinks li {\n    float: right;\n}\n\n.change-list .filtered {\n    background: white url(../img/admin/changelist-bg_rtl.gif) top left repeat-y !important;\n}\n\n.change-list .filtered table {\n    border-left: 1px solid #ddd;\n    border-right: 0px none;\n}\n\n#changelist-filter {\n    right: auto;\n    left: 0;\n    border-left: 0px none;\n    border-right: 1px solid #ddd;\n}\n\n.change-list .filtered table, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull {\n    margin-right: 0px !important;\n    margin-left: 160px !important;\n}\n\n#changelist-filter li.selected {\n    border-left: 0px none;\n    padding-left: 0px;\n    margin-left: 0;\n    border-right: 5px solid #ccc;\n    padding-right: 5px;\n    margin-right: -10px;\n}\n\n.filtered .actions {\n    border-left:1px solid #DDDDDD;\n    margin-left:160px !important;\n    border-right: 0 none;\n    margin-right:0 !important;\n}\n\n/* FORMS */\n\n.aligned label {\n    padding: 0 0 3px 1em;\n    float: right;\n}\n\n.submit-row {\n    text-align: left\n}\n\n.submit-row p.deletelink-box {\n    float: right;\n}\n\n.submit-row .deletelink {\n    background: url(../img/admin/icon_deletelink.gif) 0 50% no-repeat;\n    padding-right: 14px;\n}\n\n.vDateField, .vTimeField {\n    margin-left: 2px;\n}\n\nform ul.inline li {\n    float: right;\n    padding-right: 0;\n    padding-left: 7px;\n}\n\ninput[type=submit].default, .submit-row input.default {\n    float: left;\n}\n\nfieldset .field-box {\n    float: right;\n    margin-left: 20px;\n}\n\n.errorlist li {\n    background-position: 100% .3em;\n    padding: 4px 25px 4px 5px;\n}\n\n.errornote {\n    background-position: 100% .3em;\n    padding: 4px 25px 4px 5px;\n}\n\n/* WIDGETS */\n\n.calendarnav-previous {\n    top: 0;\n    left: auto;\n    right: 0;\n}\n\n.calendarnav-next {\n    top: 0;\n    right: auto;\n    left: 0;\n}\n\n.calendar caption, .calendarbox h2 {\n    text-align: center;\n}\n\n.selector {\n    float: right;\n}\n\n.selector .selector-filter {\n    text-align: right;\n}\n\n.inline-deletelink {\n    float: left;\n}\n\n/* MISC */\n\n.inline-related h2, .inline-group h2 {\n    text-align: right\n}\n\n.inline-related h3 span.delete {\n    padding-right: 20px;\n    padding-left: inherit;\n    left: 10px;\n    right: inherit;\n}\n\n.inline-related h3 span.delete label {\n    margin-left: inherit;\n    margin-right: 2px;\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/css/widgets.css",
    "content": "/* SELECTOR (FILTER INTERFACE) */\n\n.selector {\n    width: 580px;\n    float: left;\n}\n\n.selector select {\n    width: 270px;\n    height: 17.2em;\n}\n\n.selector-available, .selector-chosen {\n    float: left;\n    width: 270px;\n    text-align: center;\n    margin-bottom: 5px;\n}\n\n.selector-available h2, .selector-chosen h2 {\n    border: 1px solid #ccc;\n}\n\n.selector .selector-available h2 {\n    background: white url(../img/admin/nav-bg.gif) bottom left repeat-x;\n    color: #666;\n}\n\n.selector .selector-filter {\n    background: white;\n    border: 1px solid #ccc;\n    border-width: 0 1px;\n    padding: 3px;\n    color: #999;\n    font-size: 10px;\n    margin: 0;\n    text-align: left;\n}\n\n.selector .selector-chosen .selector-filter {\n    padding: 4px 5px;\n}\n\n.selector .selector-available input {\n    width: 230px;\n}\n\n.selector ul.selector-chooser {\n    float: left;\n    width: 22px;\n    height: 50px;\n    background: url(../img/admin/chooser-bg.gif) top center no-repeat;\n    margin: 8em 3px 0 3px;\n    padding: 0;\n}\n\n.selector-chooser li {\n    margin: 0;\n    padding: 3px;\n    list-style-type: none;\n}\n\n.selector select {\n    margin-bottom: 5px;\n    margin-top: 0;\n}\n\n.selector-add, .selector-remove {\n    width: 16px;\n    height: 16px;\n    display: block;\n    text-indent: -3000px;\n    overflow: hidden;\n}\n\n.selector-add {\n    background: url(../img/admin/selector-add.gif) top center no-repeat;\n    margin-bottom: 2px;\n}\n\n.selector-remove {\n    background: url(../img/admin/selector-remove.gif) top center no-repeat;\n}\n\na.selector-chooseall, a.selector-clearall {\n    display: block;\n    width: 6em;\n    text-align: left;\n    margin-left: auto;\n    margin-right: auto;\n    font-weight: bold;\n    color: #666;\n    padding: 3px 0 3px 18px;\n}\n\na.selector-chooseall:hover, a.selector-clearall:hover {\n    color: #036;\n}\n\na.selector-chooseall {\n    width: 7em;\n    background: url(../img/admin/selector-addall.gif) left center no-repeat;\n}\n\na.selector-clearall {\n    background: url(../img/admin/selector-removeall.gif) left center no-repeat;\n}\n\n\n/* STACKED SELECTORS */\n\n.stacked {\n    float: left;\n    width: 500px;\n}\n\n.stacked select {\n    width: 480px;\n    height: 10.1em;\n}\n\n.stacked .selector-available, .stacked .selector-chosen {\n    width: 480px;\n}\n\n.stacked .selector-available {\n    margin-bottom: 0;\n}\n\n.stacked .selector-available input {\n    width: 442px;\n}\n\n.stacked ul.selector-chooser {\n    height: 22px;\n    width: 50px;\n    margin: 0 0 3px 40%;\n    background: url(../img/admin/chooser_stacked-bg.gif) top center no-repeat;\n}\n\n.stacked .selector-chooser li {\n    float: left;\n    padding: 3px 3px 3px 5px;\n}\n\n.stacked .selector-chooseall, .stacked .selector-clearall {\n    display: none;\n}\n\n.stacked .selector-add {\n    background-image: url(../img/admin/selector_stacked-add.gif);\n}\n\n.stacked .selector-remove {\n    background-image: url(../img/admin/selector_stacked-remove.gif);\n}\n\n\n/* DATE AND TIME */\n\np.datetime {\n    line-height: 20px;\n    margin: 0;\n    padding: 0;\n    color: #666;\n    font-size: 11px;\n    font-weight: bold;\n}\n\n.datetime span {\n    font-size: 11px;\n    color: #ccc;\n    font-weight: normal;\n    white-space: nowrap;\n}\n\ntable p.datetime {\n    font-size: 10px;\n    margin-left: 0;\n    padding-left: 0;\n}\n\n/* FILE UPLOADS */\n\np.file-upload {\n    line-height: 20px;\n    margin: 0;\n    padding: 0;\n    color: #666;\n    font-size: 11px;\n    font-weight: bold;\n}\n\n.file-upload a {\n    font-weight: normal;\n}\n\n.file-upload .deletelink {\n    margin-left: 5px;\n}\n\n/* CALENDARS & CLOCKS */\n\n.calendarbox, .clockbox {\n    margin: 5px auto;\n    font-size: 11px;\n    width: 16em;\n    text-align: center;\n    background: white;\n    position: relative;\n}\n\n.clockbox {\n    width: auto;\n}\n\n.calendar {\n    margin: 0;\n    padding: 0;\n}\n\n.calendar table {\n    margin: 0;\n    padding: 0;\n    border-collapse: collapse;\n    background: white;\n    width: 99%;\n}\n\n.calendar caption, .calendarbox h2 {\n    margin: 0;\n    font-size: 11px;\n    text-align: center;\n    border-top: none;\n}\n\n.calendar th {\n    font-size: 10px;\n    color: #666;\n    padding: 2px 3px;\n    text-align: center;\n    background: #e1e1e1 url(../img/admin/nav-bg.gif) 0 50% repeat-x;\n    border-bottom: 1px solid #ddd;\n}\n\n.calendar td {\n    font-size: 11px;\n    text-align: center;\n    padding: 0;\n    border-top: 1px solid #eee;\n    border-bottom: none;\n}\n\n.calendar td.selected a {\n    background: #C9DBED;\n}\n\n.calendar td.nonday {\n    background: #efefef;\n}\n\n.calendar td.today a {\n    background: #ffc;\n}\n\n.calendar td a, .timelist a {\n    display: block;\n    font-weight: bold;\n    padding: 4px;\n    text-decoration: none;\n    color: #444;\n}\n\n.calendar td a:hover, .timelist a:hover {\n    background: #5b80b2;\n    color: white;\n}\n\n.calendar td a:active, .timelist a:active {\n    background: #036;\n    color: white;\n}\n\n.calendarnav {\n    font-size: 10px;\n    text-align: center;\n    color: #ccc;\n    margin: 0;\n    padding: 1px 3px;\n}\n\n.calendarnav a:link, #calendarnav a:visited, #calendarnav a:hover {\n    color: #999;\n}\n\n.calendar-shortcuts {\n    background: white;\n    font-size: 10px;\n    line-height: 11px;\n    border-top: 1px solid #eee;\n    padding: 3px 0 4px;\n    color: #ccc;\n}\n\n.calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {\n    display: block;\n    position: absolute;\n    font-weight: bold;\n    font-size: 12px;\n    background: #C9DBED url(../img/admin/default-bg.gif) bottom left repeat-x;\n    padding: 1px 4px 2px 4px;\n    color: white;\n}\n\n.calendarnav-previous:hover, .calendarnav-next:hover {\n    background: #036;\n}\n\n.calendarnav-previous {\n    top: 0;\n    left: 0;\n}\n\n.calendarnav-next {\n    top: 0;\n    right: 0;\n}\n\n.calendar-cancel {\n    margin: 0 !important;\n    padding: 0;\n    font-size: 10px;\n    background: #e1e1e1 url(../img/admin/nav-bg.gif) 0 50% repeat-x;\n    border-top: 1px solid #ddd;\n}\n\n.calendar-cancel a {\n    padding: 2px;\n    color: #999;\n}\n\nul.timelist, .timelist li {\n    list-style-type: none;\n    margin: 0;\n    padding: 0;\n}\n\n.timelist a {\n    padding: 2px;\n}\n\n/* INLINE ORDERER */\n\nul.orderer {\n    position: relative;\n    padding: 0 !important;\n    margin: 0 !important;\n    list-style-type: none;\n}\n\nul.orderer li {\n    list-style-type: none;\n    display: block;\n    padding: 0;\n    margin: 0;\n    border: 1px solid #bbb;\n    border-width: 0 1px 1px 0;\n    white-space: nowrap;\n    overflow: hidden;\n    background: #e2e2e2 url(../img/admin/nav-bg-grabber.gif) repeat-y;\n}\n\nul.orderer li:hover {\n    cursor: move;\n    background-color: #ddd;\n}\n\nul.orderer li a.selector {\n    margin-left: 12px;\n    overflow: hidden;\n    width: 83%;\n    font-size: 10px !important;\n    padding: 0.6em 0;\n}\n\nul.orderer li a:link, ul.orderer li a:visited {\n    color: #333;\n}\n\nul.orderer li .inline-deletelink {\n    position: absolute;\n    right: 4px;\n    margin-top: 0.6em;\n}\n\nul.orderer li.selected {\n    background-color: #f8f8f8;\n    border-right-color: #f8f8f8;\n}\n\nul.orderer li.deleted {\n    background: #bbb url(../img/admin/deleted-overlay.gif);\n}\n\nul.orderer li.deleted a:link, ul.orderer li.deleted a:visited {\n    color: #888;\n}\n\nul.orderer li.deleted .inline-deletelink {\n    background-image: url(../img/admin/inline-restore.png);\n}\n\nul.orderer li.deleted:hover, ul.orderer li.deleted a.selector:hover {\n    cursor: default;\n}\n\n/* EDIT INLINE */\n\n.inline-deletelink {\n    float: right;\n    text-indent: -9999px;\n    background: transparent url(../img/admin/inline-delete.png) no-repeat;\n    width: 15px;\n    height: 15px;\n    border: 0px none;\n    outline: 0; /* Remove dotted border around link */\n}\n\n.inline-deletelink:hover {\n    background-position: -15px 0;\n    cursor: pointer;\n}\n\n.editinline button.addlink {\n    border: 0px none;\n    color: #5b80b2;\n    font-size: 100%;\n    cursor: pointer;\n}\n\n.editinline button.addlink:hover {\n    color: #036;\n    cursor: pointer;\n}\n\n.editinline table .help {\n    text-align: right;\n    float: right;\n    padding-left: 2em;\n}\n\n.editinline tfoot .addlink {\n    white-space: nowrap;\n}\n\n.editinline table thead th:last-child {\n    border-left: none;\n}\n\n.editinline tr.deleted {\n    background: #ddd url(../img/admin/deleted-overlay.gif);\n}\n\n.editinline tr.deleted .inline-deletelink {\n    background-image: url(../img/admin/inline-restore.png);\n}\n\n.editinline tr.deleted td:hover {\n    cursor: default;\n}\n\n.editinline tr.deleted td:first-child {\n    background-image: none !important;\n}\n\n/* EDIT INLINE - STACKED */\n\n.editinline-stacked {\n    min-width: 758px;\n}\n\n.editinline-stacked .inline-object {\n    margin-left: 210px;\n    background: white;\n}\n\n.editinline-stacked .inline-source {\n    float: left;\n    width: 200px;\n    background: #f8f8f8;\n}\n\n.editinline-stacked .inline-splitter {\n    float: left;\n    width: 9px;\n    background: #f8f8f8 url(../img/admin/inline-splitter-bg.gif) 50% 50% no-repeat;\n    border-right: 1px solid #ccc;\n}\n\n.editinline-stacked .controls {\n    clear: both;\n    background: #e1e1e1 url(../img/admin/nav-bg.gif) top left repeat-x;\n    padding: 3px 4px;\n    font-size: 11px;\n    border-top: 1px solid #ddd;\n}\n\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/SelectBox.js",
    "content": "var SelectBox = {\n    cache: new Object(),\n    init: function(id) {\n        var box = document.getElementById(id);\n        var node;\n        SelectBox.cache[id] = new Array();\n        var cache = SelectBox.cache[id];\n        for (var i = 0; (node = box.options[i]); i++) {\n            cache.push({value: node.value, text: node.text, displayed: 1});\n        }\n    },\n    redisplay: function(id) {\n        // Repopulate HTML select box from cache\n        var box = document.getElementById(id);\n        box.options.length = 0; // clear all options\n        for (var i = 0, j = SelectBox.cache[id].length; i < j; i++) {\n            var node = SelectBox.cache[id][i];\n            if (node.displayed) {\n                box.options[box.options.length] = new Option(node.text, node.value, false, false);\n            }\n        }\n    },\n    filter: function(id, text) {\n        // Redisplay the HTML select box, displaying only the choices containing ALL\n        // the words in text. (It's an AND search.)\n        var tokens = text.toLowerCase().split(/\\s+/);\n        var node, token;\n        for (var i = 0; (node = SelectBox.cache[id][i]); i++) {\n            node.displayed = 1;\n            for (var j = 0; (token = tokens[j]); j++) {\n                if (node.text.toLowerCase().indexOf(token) == -1) {\n                    node.displayed = 0;\n                }\n            }\n        }\n        SelectBox.redisplay(id);\n    },\n    delete_from_cache: function(id, value) {\n        var node, delete_index = null;\n        for (var i = 0; (node = SelectBox.cache[id][i]); i++) {\n            if (node.value == value) {\n                delete_index = i;\n                break;\n            }\n        }\n        var j = SelectBox.cache[id].length - 1;\n        for (var i = delete_index; i < j; i++) {\n            SelectBox.cache[id][i] = SelectBox.cache[id][i+1];\n        }\n        SelectBox.cache[id].length--;\n    },\n    add_to_cache: function(id, option) {\n        SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1});\n    },\n    cache_contains: function(id, value) {\n        // Check if an item is contained in the cache\n        var node;\n        for (var i = 0; (node = SelectBox.cache[id][i]); i++) {\n            if (node.value == value) {\n                return true;\n            }\n        }\n        return false;\n    },\n    move: function(from, to) {\n        var from_box = document.getElementById(from);\n        var to_box = document.getElementById(to);\n        var option;\n        for (var i = 0; (option = from_box.options[i]); i++) {\n            if (option.selected && SelectBox.cache_contains(from, option.value)) {\n                SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1});\n                SelectBox.delete_from_cache(from, option.value);\n            }\n        }\n        SelectBox.redisplay(from);\n        SelectBox.redisplay(to);\n    },\n    move_all: function(from, to) {\n        var from_box = document.getElementById(from);\n        var to_box = document.getElementById(to);\n        var option;\n        for (var i = 0; (option = from_box.options[i]); i++) {\n            if (SelectBox.cache_contains(from, option.value)) {\n                SelectBox.add_to_cache(to, {value: option.value, text: option.text, displayed: 1});\n                SelectBox.delete_from_cache(from, option.value);\n            }\n        }\n        SelectBox.redisplay(from);\n        SelectBox.redisplay(to);\n    },\n    sort: function(id) {\n        SelectBox.cache[id].sort( function(a, b) {\n            a = a.text.toLowerCase();\n            b = b.text.toLowerCase();\n            try {\n                if (a > b) return 1;\n                if (a < b) return -1;\n            }\n            catch (e) {\n                // silently fail on IE 'unknown' exception\n            }\n            return 0;\n        } );\n    },\n    select_all: function(id) {\n        var box = document.getElementById(id);\n        for (var i = 0; i < box.options.length; i++) {\n            box.options[i].selected = 'selected';\n        }\n    }\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/SelectFilter2.js",
    "content": "/*\nSelectFilter2 - Turns a multiple-select box into a filter interface.\n\nDifferent than SelectFilter because this is coupled to the admin framework.\n\nRequires core.js, SelectBox.js and addevent.js.\n*/\n\nfunction findForm(node) {\n    // returns the node of the form containing the given node\n    if (node.tagName.toLowerCase() != 'form') {\n        return findForm(node.parentNode);\n    }\n    return node;\n}\n\nvar SelectFilter = {\n    init: function(field_id, field_name, is_stacked, admin_media_prefix) {\n        if (field_id.match(/__prefix__/)){\n            // Don't intialize on empty forms.\n            return;\n        }\n        var from_box = document.getElementById(field_id);\n        from_box.id += '_from'; // change its ID\n        from_box.className = 'filtered';\n\n        // Remove <p class=\"info\">, because it just gets in the way.\n        var ps = from_box.parentNode.getElementsByTagName('p');\n        for (var i=0; i<ps.length; i++) {\n            from_box.parentNode.removeChild(ps[i]);\n        }\n\n        // <div class=\"selector\"> or <div class=\"selector stacked\">\n        var selector_div = quickElement('div', from_box.parentNode);\n        selector_div.className = is_stacked ? 'selector stacked' : 'selector';\n\n        // <div class=\"selector-available\">\n        var selector_available = quickElement('div', selector_div, '');\n        selector_available.className = 'selector-available';\n        quickElement('h2', selector_available, interpolate(gettext('Available %s'), [field_name]));\n        var filter_p = quickElement('p', selector_available, '');\n        filter_p.className = 'selector-filter';\n        quickElement('img', filter_p, '', 'src', admin_media_prefix + 'img/admin/selector-search.gif');\n        filter_p.appendChild(document.createTextNode(' '));\n        var filter_input = quickElement('input', filter_p, '', 'type', 'text');\n        filter_input.id = field_id + '_input';\n        selector_available.appendChild(from_box);\n        var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'href', 'javascript: (function(){ SelectBox.move_all(\"' + field_id + '_from\", \"' + field_id + '_to\"); })()');\n        choose_all.className = 'selector-chooseall';\n\n        // <ul class=\"selector-chooser\">\n        var selector_chooser = quickElement('ul', selector_div, '');\n        selector_chooser.className = 'selector-chooser';\n        var add_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Add'), 'href', 'javascript: (function(){ SelectBox.move(\"' + field_id + '_from\",\"' + field_id + '_to\");})()');\n        add_link.className = 'selector-add';\n        var remove_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Remove'), 'href', 'javascript: (function(){ SelectBox.move(\"' + field_id + '_to\",\"' + field_id + '_from\");})()');\n        remove_link.className = 'selector-remove';\n\n        // <div class=\"selector-chosen\">\n        var selector_chosen = quickElement('div', selector_div, '');\n        selector_chosen.className = 'selector-chosen';\n        quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s'), [field_name]));\n        var selector_filter = quickElement('p', selector_chosen, gettext('Select your choice(s) and click '));\n        selector_filter.className = 'selector-filter';\n        quickElement('img', selector_filter, '', 'src', admin_media_prefix + (is_stacked ? 'img/admin/selector_stacked-add.gif':'img/admin/selector-add.gif'), 'alt', 'Add');\n        var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));\n        to_box.className = 'filtered';\n        var clear_all = quickElement('a', selector_chosen, gettext('Clear all'), 'href', 'javascript: (function() { SelectBox.move_all(\"' + field_id + '_to\", \"' + field_id + '_from\");})()');\n        clear_all.className = 'selector-clearall';\n\n        from_box.setAttribute('name', from_box.getAttribute('name') + '_old');\n\n        // Set up the JavaScript event handlers for the select box filter interface\n        addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });\n        addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });\n        addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); });\n        addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); });\n        addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });\n        SelectBox.init(field_id + '_from');\n        SelectBox.init(field_id + '_to');\n        // Move selected from_box options to to_box\n        SelectBox.move(field_id + '_from', field_id + '_to');\n    },\n    filter_key_up: function(event, field_id) {\n        from = document.getElementById(field_id + '_from');\n        // don't submit form if user pressed Enter\n        if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {\n            from.selectedIndex = 0;\n            SelectBox.move(field_id + '_from', field_id + '_to');\n            from.selectedIndex = 0;\n            return false;\n        }\n        var temp = from.selectedIndex;\n        SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);\n        from.selectedIndex = temp;\n        return true;\n    },\n    filter_key_down: function(event, field_id) {\n        from = document.getElementById(field_id + '_from');\n        // right arrow -- move across\n        if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {\n            var old_index = from.selectedIndex;\n            SelectBox.move(field_id + '_from', field_id + '_to');\n            from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;\n            return false;\n        }\n        // down arrow -- wrap around\n        if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {\n            from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;\n        }\n        // up arrow -- wrap around\n        if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {\n            from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/actions.js",
    "content": "(function($) {\n\t$.fn.actions = function(opts) {\n\t\tvar options = $.extend({}, $.fn.actions.defaults, opts);\n\t\tvar actionCheckboxes = $(this);\n\t\tvar list_editable_changed = false;\n\t\tchecker = function(checked) {\n\t\t\tif (checked) {\n\t\t\t\tshowQuestion();\n\t\t\t} else {\n\t\t\t\treset();\n\t\t\t}\n\t\t\t$(actionCheckboxes).attr(\"checked\", checked)\n\t\t\t\t.parent().parent().toggleClass(options.selectedClass, checked);\n\t\t}\n\t\tupdateCounter = function() {\n\t\t\tvar sel = $(actionCheckboxes).filter(\":checked\").length;\n\t\t\t$(options.counterContainer).html(interpolate(\n\t\t\tngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {\n\t\t\t\tsel: sel,\n\t\t\t\tcnt: _actions_icnt\n\t\t\t}, true));\n\t\t\t$(options.allToggle).attr(\"checked\", function() {\n\t\t\t\tif (sel == actionCheckboxes.length) {\n\t\t\t\t\tvalue = true;\n\t\t\t\t\tshowQuestion();\n\t\t\t\t} else {\n\t\t\t\t\tvalue = false;\n\t\t\t\t\tclearAcross();\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t});\n\t\t}\n\t\tshowQuestion = function() {\n\t\t\t$(options.acrossClears).hide();\n\t\t\t$(options.acrossQuestions).show();\n\t\t\t$(options.allContainer).hide();\n\t\t}\n\t\tshowClear = function() {\n\t\t\t$(options.acrossClears).show();\n\t\t\t$(options.acrossQuestions).hide();\n\t\t\t$(options.actionContainer).toggleClass(options.selectedClass);\n\t\t\t$(options.allContainer).show();\n\t\t\t$(options.counterContainer).hide();\n\t\t}\n\t\treset = function() {\n\t\t\t$(options.acrossClears).hide();\n\t\t\t$(options.acrossQuestions).hide();\n\t\t\t$(options.allContainer).hide();\n\t\t\t$(options.counterContainer).show();\n\t\t}\n\t\tclearAcross = function() {\n\t\t\treset();\n\t\t\t$(options.acrossInput).val(0);\n\t\t\t$(options.actionContainer).removeClass(options.selectedClass);\n\t\t}\n\t\t// Show counter by default\n\t\t$(options.counterContainer).show();\n\t\t// Check state of checkboxes and reinit state if needed\n\t\t$(this).filter(\":checked\").each(function(i) {\n\t\t\t$(this).parent().parent().toggleClass(options.selectedClass);\n\t\t\tupdateCounter();\n\t\t\tif ($(options.acrossInput).val() == 1) {\n\t\t\t\tshowClear();\n\t\t\t}\n\t\t});\n\t\t$(options.allToggle).show().click(function() {\n\t\t\tchecker($(this).attr(\"checked\"));\n\t\t\tupdateCounter();\n\t\t});\n\t\t$(\"div.actions span.question a\").click(function(event) {\n\t\t\tevent.preventDefault();\n\t\t\t$(options.acrossInput).val(1);\n\t\t\tshowClear();\n\t\t});\n\t\t$(\"div.actions span.clear a\").click(function(event) {\n\t\t\tevent.preventDefault();\n\t\t\t$(options.allToggle).attr(\"checked\", false);\n\t\t\tclearAcross();\n\t\t\tchecker(0);\n\t\t\tupdateCounter();\n\t\t});\n\t\tlastChecked = null;\n\t\t$(actionCheckboxes).click(function(event) {\n\t\t\tif (!event) { var event = window.event; }\n\t\t\tvar target = event.target ? event.target : event.srcElement;\n\t\t\tif (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey == true) {\n\t\t\t\tvar inrange = false;\n\t\t\t\t$(lastChecked).attr(\"checked\", target.checked)\n\t\t\t\t\t.parent().parent().toggleClass(options.selectedClass, target.checked);\n\t\t\t\t$(actionCheckboxes).each(function() {\n\t\t\t\t\tif ($.data(this) == $.data(lastChecked) || $.data(this) == $.data(target)) {\n\t\t\t\t\t\tinrange = (inrange) ? false : true;\n\t\t\t\t\t}\n\t\t\t\t\tif (inrange) {\n\t\t\t\t\t\t$(this).attr(\"checked\", target.checked)\n\t\t\t\t\t\t\t.parent().parent().toggleClass(options.selectedClass, target.checked);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\t$(target).parent().parent().toggleClass(options.selectedClass, target.checked);\n\t\t\tlastChecked = target;\n\t\t\tupdateCounter();\n\t\t});\n\t\t$('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() {\n\t\t\tlist_editable_changed = true;\n\t\t});\n\t\t$('form#changelist-form button[name=\"index\"]').click(function(event) {\n\t\t\tif (list_editable_changed) {\n\t\t\t\treturn confirm(gettext(\"You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.\"));\n\t\t\t}\n\t\t});\n\t\t$('form#changelist-form input[name=\"_save\"]').click(function(event) {\n\t\t\tvar action_changed = false;\n\t\t\t$('div.actions select option:selected').each(function() {\n\t\t\t\tif ($(this).val()) {\n\t\t\t\t\taction_changed = true;\n\t\t\t\t}\n\t\t\t});\n\t\t\tif (action_changed) {\n\t\t\t\tif (list_editable_changed) {\n\t\t\t\t\treturn confirm(gettext(\"You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.\"));\n\t\t\t\t} else {\n\t\t\t\t\treturn confirm(gettext(\"You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button.\"));\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\t/* Setup plugin defaults */\n\t$.fn.actions.defaults = {\n\t\tactionContainer: \"div.actions\",\n\t\tcounterContainer: \"span.action-counter\",\n\t\tallContainer: \"div.actions span.all\",\n\t\tacrossInput: \"div.actions input.select-across\",\n\t\tacrossQuestions: \"div.actions span.question\",\n\t\tacrossClears: \"div.actions span.clear\",\n\t\tallToggle: \"#action-toggle\",\n\t\tselectedClass: \"selected\"\n\t}\n})(django.jQuery);\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/admin/DateTimeShortcuts.js",
    "content": "// Inserts shortcut buttons after all of the following:\n//     <input type=\"text\" class=\"vDateField\">\n//     <input type=\"text\" class=\"vTimeField\">\n\nvar DateTimeShortcuts = {\n    calendars: [],\n    calendarInputs: [],\n    clockInputs: [],\n    calendarDivName1: 'calendarbox', // name of calendar <div> that gets toggled\n    calendarDivName2: 'calendarin',  // name of <div> that contains calendar\n    calendarLinkName: 'calendarlink',// name of the link that is used to toggle\n    clockDivName: 'clockbox',        // name of clock <div> that gets toggled\n    clockLinkName: 'clocklink',      // name of the link that is used to toggle\n    shortCutsClass: 'datetimeshortcuts', // class of the clock and cal shortcuts\n    admin_media_prefix: '',\n    init: function() {\n        // Get admin_media_prefix by grabbing it off the window object. It's\n        // set in the admin/base.html template, so if it's not there, someone's\n        // overridden the template. In that case, we'll set a clearly-invalid\n        // value in the hopes that someone will examine HTTP requests and see it.\n        if (window.__admin_media_prefix__ != undefined) {\n            DateTimeShortcuts.admin_media_prefix = window.__admin_media_prefix__;\n        } else {\n            DateTimeShortcuts.admin_media_prefix = '/missing-admin-media-prefix/';\n        }\n\n        var inputs = document.getElementsByTagName('input');\n        for (i=0; i<inputs.length; i++) {\n            var inp = inputs[i];\n            if (inp.getAttribute('type') == 'text' && inp.className.match(/vTimeField/)) {\n                DateTimeShortcuts.addClock(inp);\n            }\n            else if (inp.getAttribute('type') == 'text' && inp.className.match(/vDateField/)) {\n                DateTimeShortcuts.addCalendar(inp);\n            }\n        }\n    },\n    // Add clock widget to a given field\n    addClock: function(inp) {\n        var num = DateTimeShortcuts.clockInputs.length;\n        DateTimeShortcuts.clockInputs[num] = inp;\n\n        // Shortcut links (clock icon and \"Now\" link)\n        var shortcuts_span = document.createElement('span');\n        shortcuts_span.className = DateTimeShortcuts.shortCutsClass;\n        inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);\n        var now_link = document.createElement('a');\n        now_link.setAttribute('href', \"javascript:DateTimeShortcuts.handleClockQuicklink(\" + num + \", new Date().strftime('\" + get_format('TIME_INPUT_FORMATS')[0] + \"'));\");\n        now_link.appendChild(document.createTextNode(gettext('Now')));\n        var clock_link = document.createElement('a');\n        clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');\n        clock_link.id = DateTimeShortcuts.clockLinkName + num;\n        quickElement('img', clock_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_clock.gif', 'alt', gettext('Clock'));\n        shortcuts_span.appendChild(document.createTextNode('\\240'));\n        shortcuts_span.appendChild(now_link);\n        shortcuts_span.appendChild(document.createTextNode('\\240|\\240'));\n        shortcuts_span.appendChild(clock_link);\n\n        // Create clock link div\n        //\n        // Markup looks like:\n        // <div id=\"clockbox1\" class=\"clockbox module\">\n        //     <h2>Choose a time</h2>\n        //     <ul class=\"timelist\">\n        //         <li><a href=\"#\">Now</a></li>\n        //         <li><a href=\"#\">Midnight</a></li>\n        //         <li><a href=\"#\">6 a.m.</a></li>\n        //         <li><a href=\"#\">Noon</a></li>\n        //     </ul>\n        //     <p class=\"calendar-cancel\"><a href=\"#\">Cancel</a></p>\n        // </div>\n\n        var clock_box = document.createElement('div');\n        clock_box.style.display = 'none';\n        clock_box.style.position = 'absolute';\n        clock_box.className = 'clockbox module';\n        clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num);\n        document.body.appendChild(clock_box);\n        addEvent(clock_box, 'click', DateTimeShortcuts.cancelEventPropagation);\n\n        quickElement('h2', clock_box, gettext('Choose a time'));\n        time_list = quickElement('ul', clock_box, '');\n        time_list.className = 'timelist';\n        time_format = get_format('TIME_INPUT_FORMATS')[0];\n        quickElement(\"a\", quickElement(\"li\", time_list, \"\"), gettext(\"Now\"), \"href\", \"javascript:DateTimeShortcuts.handleClockQuicklink(\" + num + \", new Date().strftime('\" + time_format + \"'));\");\n        quickElement(\"a\", quickElement(\"li\", time_list, \"\"), gettext(\"Midnight\"), \"href\", \"javascript:DateTimeShortcuts.handleClockQuicklink(\" + num + \", new Date(1970,1,1,0,0,0,0).strftime('\" + time_format + \"'));\");\n        quickElement(\"a\", quickElement(\"li\", time_list, \"\"), gettext(\"6 a.m.\"), \"href\", \"javascript:DateTimeShortcuts.handleClockQuicklink(\" + num + \", new Date(1970,1,1,6,0,0,0).strftime('\" + time_format + \"'));\");\n        quickElement(\"a\", quickElement(\"li\", time_list, \"\"), gettext(\"Noon\"), \"href\", \"javascript:DateTimeShortcuts.handleClockQuicklink(\" + num + \", new Date(1970,1,1,12,0,0,0).strftime('\" + time_format + \"'));\");\n\n        cancel_p = quickElement('p', clock_box, '');\n        cancel_p.className = 'calendar-cancel';\n        quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');');\n    },\n    openClock: function(num) {\n        var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num)\n        var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num)\n\n        // Recalculate the clockbox position\n        // is it left-to-right or right-to-left layout ?\n        if (getStyle(document.body,'direction')!='rtl') {\n            clock_box.style.left = findPosX(clock_link) + 17 + 'px';\n        }\n        else {\n            // since style's width is in em, it'd be tough to calculate\n            // px value of it. let's use an estimated px for now\n            // TODO: IE returns wrong value for findPosX when in rtl mode\n            //       (it returns as it was left aligned), needs to be fixed.\n            clock_box.style.left = findPosX(clock_link) - 110 + 'px';\n        }\n        clock_box.style.top = Math.max(0, findPosY(clock_link) - 30) + 'px';\n\n        // Show the clock box\n        clock_box.style.display = 'block';\n        addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissClock(num); return true; });\n    },\n    dismissClock: function(num) {\n       document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';\n       window.document.onclick = null;\n    },\n    handleClockQuicklink: function(num, val) {\n       DateTimeShortcuts.clockInputs[num].value = val;\n       DateTimeShortcuts.clockInputs[num].focus();\n       DateTimeShortcuts.dismissClock(num);\n    },\n    // Add calendar widget to a given field.\n    addCalendar: function(inp) {\n        var num = DateTimeShortcuts.calendars.length;\n\n        DateTimeShortcuts.calendarInputs[num] = inp;\n\n        // Shortcut links (calendar icon and \"Today\" link)\n        var shortcuts_span = document.createElement('span');\n        shortcuts_span.className = DateTimeShortcuts.shortCutsClass;\n        inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);\n        var today_link = document.createElement('a');\n        today_link.setAttribute('href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');\n        today_link.appendChild(document.createTextNode(gettext('Today')));\n        var cal_link = document.createElement('a');\n        cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');');\n        cal_link.id = DateTimeShortcuts.calendarLinkName + num;\n        quickElement('img', cal_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_calendar.gif', 'alt', gettext('Calendar'));\n        shortcuts_span.appendChild(document.createTextNode('\\240'));\n        shortcuts_span.appendChild(today_link);\n        shortcuts_span.appendChild(document.createTextNode('\\240|\\240'));\n        shortcuts_span.appendChild(cal_link);\n\n        // Create calendarbox div.\n        //\n        // Markup looks like:\n        //\n        // <div id=\"calendarbox3\" class=\"calendarbox module\">\n        //     <h2>\n        //           <a href=\"#\" class=\"link-previous\">&lsaquo;</a>\n        //           <a href=\"#\" class=\"link-next\">&rsaquo;</a> February 2003\n        //     </h2>\n        //     <div class=\"calendar\" id=\"calendarin3\">\n        //         <!-- (cal) -->\n        //     </div>\n        //     <div class=\"calendar-shortcuts\">\n        //          <a href=\"#\">Yesterday</a> | <a href=\"#\">Today</a> | <a href=\"#\">Tomorrow</a>\n        //     </div>\n        //     <p class=\"calendar-cancel\"><a href=\"#\">Cancel</a></p>\n        // </div>\n        var cal_box = document.createElement('div');\n        cal_box.style.display = 'none';\n        cal_box.style.position = 'absolute';\n        cal_box.className = 'calendarbox module';\n        cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num);\n        document.body.appendChild(cal_box);\n        addEvent(cal_box, 'click', DateTimeShortcuts.cancelEventPropagation);\n\n        // next-prev links\n        var cal_nav = quickElement('div', cal_box, '');\n        var cal_nav_prev = quickElement('a', cal_nav, '<', 'href', 'javascript:DateTimeShortcuts.drawPrev('+num+');');\n        cal_nav_prev.className = 'calendarnav-previous';\n        var cal_nav_next = quickElement('a', cal_nav, '>', 'href', 'javascript:DateTimeShortcuts.drawNext('+num+');');\n        cal_nav_next.className = 'calendarnav-next';\n\n        // main box\n        var cal_main = quickElement('div', cal_box, '', 'id', DateTimeShortcuts.calendarDivName2 + num);\n        cal_main.className = 'calendar';\n        DateTimeShortcuts.calendars[num] = new Calendar(DateTimeShortcuts.calendarDivName2 + num, DateTimeShortcuts.handleCalendarCallback(num));\n        DateTimeShortcuts.calendars[num].drawCurrent();\n\n        // calendar shortcuts\n        var shortcuts = quickElement('div', cal_box, '');\n        shortcuts.className = 'calendar-shortcuts';\n        quickElement('a', shortcuts, gettext('Yesterday'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', -1);');\n        shortcuts.appendChild(document.createTextNode('\\240|\\240'));\n        quickElement('a', shortcuts, gettext('Today'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', 0);');\n        shortcuts.appendChild(document.createTextNode('\\240|\\240'));\n        quickElement('a', shortcuts, gettext('Tomorrow'), 'href', 'javascript:DateTimeShortcuts.handleCalendarQuickLink(' + num + ', +1);');\n\n        // cancel bar\n        var cancel_p = quickElement('p', cal_box, '');\n        cancel_p.className = 'calendar-cancel';\n        quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');');\n    },\n    openCalendar: function(num) {\n        var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)\n        var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)\n        var inp = DateTimeShortcuts.calendarInputs[num];\n\n        // Determine if the current value in the input has a valid date.\n        // If so, draw the calendar with that date's year and month.\n        if (inp.value) {\n            var date_parts = inp.value.split('-');\n            var year = date_parts[0];\n            var month = parseFloat(date_parts[1]);\n            if (year.match(/\\d\\d\\d\\d/) && month >= 1 && month <= 12) {\n                DateTimeShortcuts.calendars[num].drawDate(month, year);\n            }\n        }\n\n        // Recalculate the clockbox position\n        // is it left-to-right or right-to-left layout ?\n        if (getStyle(document.body,'direction')!='rtl') {\n            cal_box.style.left = findPosX(cal_link) + 17 + 'px';\n        }\n        else {\n            // since style's width is in em, it'd be tough to calculate\n            // px value of it. let's use an estimated px for now\n            // TODO: IE returns wrong value for findPosX when in rtl mode\n            //       (it returns as it was left aligned), needs to be fixed.\n            cal_box.style.left = findPosX(cal_link) - 180 + 'px';\n        }\n        cal_box.style.top = Math.max(0, findPosY(cal_link) - 75) + 'px';\n\n        cal_box.style.display = 'block';\n        addEvent(window.document, 'click', function() { DateTimeShortcuts.dismissCalendar(num); return true; });\n    },\n    dismissCalendar: function(num) {\n        document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'none';\n        window.document.onclick = null;\n    },\n    drawPrev: function(num) {\n        DateTimeShortcuts.calendars[num].drawPreviousMonth();\n    },\n    drawNext: function(num) {\n        DateTimeShortcuts.calendars[num].drawNextMonth();\n    },\n    handleCalendarCallback: function(num) {\n        format = get_format('DATE_INPUT_FORMATS')[0];\n        // the format needs to be escaped a little\n        format = format.replace('\\\\', '\\\\\\\\');\n        format = format.replace('\\r', '\\\\r');\n        format = format.replace('\\n', '\\\\n');\n        format = format.replace('\\t', '\\\\t');\n        format = format.replace(\"'\", \"\\\\'\");\n        return [\"function(y, m, d) { DateTimeShortcuts.calendarInputs[\",\n               num,\n               \"].value = new Date(y, m-1, d).strftime('\",\n               format,\n               \"');DateTimeShortcuts.calendarInputs[\",\n               num,\n               \"].focus();document.getElementById(DateTimeShortcuts.calendarDivName1+\",\n               num,\n               \").style.display='none';}\"].join('');\n    },\n    handleCalendarQuickLink: function(num, offset) {\n       var d = new Date();\n       d.setDate(d.getDate() + offset)\n       DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);\n       DateTimeShortcuts.calendarInputs[num].focus();\n       DateTimeShortcuts.dismissCalendar(num);\n    },\n    cancelEventPropagation: function(e) {\n        if (!e) e = window.event;\n        e.cancelBubble = true;\n        if (e.stopPropagation) e.stopPropagation();\n    }\n}\n\naddEvent(window, 'load', DateTimeShortcuts.init);\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/admin/RelatedObjectLookups.js",
    "content": "// Handles related-objects functionality: lookup link for raw_id_fields\n// and Add Another links.\n\nfunction html_unescape(text) {\n    // Unescape a string that was escaped using django.utils.html.escape.\n    text = text.replace(/&lt;/g, '<');\n    text = text.replace(/&gt;/g, '>');\n    text = text.replace(/&quot;/g, '\"');\n    text = text.replace(/&#39;/g, \"'\");\n    text = text.replace(/&amp;/g, '&');\n    return text;\n}\n\n// IE doesn't accept periods or dashes in the window name, but the element IDs\n// we use to generate popup window names may contain them, therefore we map them\n// to allowed characters in a reversible way so that we can locate the correct \n// element when the popup window is dismissed.\nfunction id_to_windowname(text) {\n    text = text.replace(/\\./g, '__dot__');\n    text = text.replace(/\\-/g, '__dash__');\n    return text;\n}\n\nfunction windowname_to_id(text) {\n    text = text.replace(/__dot__/g, '.');\n    text = text.replace(/__dash__/g, '-');\n    return text;\n}\n\nfunction showRelatedObjectLookupPopup(triggeringLink) {\n    var name = triggeringLink.id.replace(/^lookup_/, '');\n    name = id_to_windowname(name);\n    var href;\n    if (triggeringLink.href.search(/\\?/) >= 0) {\n        href = triggeringLink.href + '&pop=1';\n    } else {\n        href = triggeringLink.href + '?pop=1';\n    }\n    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');\n    win.focus();\n    return false;\n}\n\nfunction dismissRelatedLookupPopup(win, chosenId) {\n    var name = windowname_to_id(win.name);\n    var elem = document.getElementById(name);\n    if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {\n        elem.value += ',' + chosenId;\n    } else {\n        document.getElementById(name).value = chosenId;\n    }\n    win.close();\n}\n\nfunction showAddAnotherPopup(triggeringLink) {\n    var name = triggeringLink.id.replace(/^add_/, '');\n    name = id_to_windowname(name);\n    href = triggeringLink.href\n    if (href.indexOf('?') == -1) {\n        href += '?_popup=1';\n    } else {\n        href  += '&_popup=1';\n    }\n    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');\n    win.focus();\n    return false;\n}\n\nfunction dismissAddAnotherPopup(win, newId, newRepr) {\n    // newId and newRepr are expected to have previously been escaped by\n    // django.utils.html.escape.\n    newId = html_unescape(newId);\n    newRepr = html_unescape(newRepr);\n    var name = windowname_to_id(win.name);\n    var elem = document.getElementById(name);\n    if (elem) {\n        if (elem.nodeName == 'SELECT') {\n            var o = new Option(newRepr, newId);\n            elem.options[elem.options.length] = o;\n            o.selected = true;\n        } else if (elem.nodeName == 'INPUT') {\n            if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {\n                elem.value += ',' + newId;\n            } else {\n                elem.value = newId;\n            }\n        }\n    } else {\n        var toId = name + \"_to\";\n        elem = document.getElementById(toId);\n        var o = new Option(newRepr, newId);\n        SelectBox.add_to_cache(toId, o);\n        SelectBox.redisplay(toId);\n    }\n    win.close();\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/admin/ordering.js",
    "content": "addEvent(window, 'load', reorder_init);\n\nvar lis;\nvar top = 0;\nvar left = 0;\nvar height = 30;\n\nfunction reorder_init() {\n    lis = document.getElementsBySelector('ul#orderthese li');\n    var input = document.getElementsBySelector('input[name=order_]')[0];\n    setOrder(input.value.split(','));\n    input.disabled = true;\n    draw();\n    // Now initialise the dragging behaviour\n    var limit = (lis.length - 1) * height;\n    for (var i = 0; i < lis.length; i++) {\n        var li = lis[i];\n        var img = document.getElementById('handle'+li.id);\n        li.style.zIndex = 1;\n        Drag.init(img, li, left + 10, left + 10, top + 10, top + 10 + limit);\n        li.onDragStart = startDrag;\n        li.onDragEnd = endDrag;\n        img.style.cursor = 'move';\n    }\n}\n\nfunction submitOrderForm() {\n    var inputOrder = document.getElementsBySelector('input[name=order_]')[0];\n    inputOrder.value = getOrder();\n    inputOrder.disabled=false;\n}\n\nfunction startDrag() {\n    this.style.zIndex = '10';\n    this.className = 'dragging';\n}\n\nfunction endDrag(x, y) {\n    this.style.zIndex = '1';\n    this.className = '';\n    // Work out how far along it has been dropped, using x co-ordinate\n    var oldIndex = this.index;\n    var newIndex = Math.round((y - 10 - top) / height);\n    // 'Snap' to the correct position\n    this.style.top = (10 + top + newIndex * height) + 'px';\n    this.index = newIndex;\n    moveItem(oldIndex, newIndex);\n}\n\nfunction moveItem(oldIndex, newIndex) {\n    // Swaps two items, adjusts the index and left co-ord for all others\n    if (oldIndex == newIndex) {\n        return; // Nothing to swap;\n    }\n    var direction, lo, hi;\n    if (newIndex > oldIndex) {\n        lo = oldIndex;\n        hi = newIndex;\n        direction = -1;\n    } else {\n        direction = 1;\n        hi = oldIndex;\n        lo = newIndex;\n    }\n    var lis2 = new Array(); // We will build the new order in this array\n    for (var i = 0; i < lis.length; i++) {\n        if (i < lo || i > hi) {\n            // Position of items not between the indexes is unaffected\n            lis2[i] = lis[i];\n            continue;\n        } else if (i == newIndex) {\n            lis2[i] = lis[oldIndex];\n            continue;\n        } else {\n            // Item is between the two indexes - move it along 1\n            lis2[i] = lis[i - direction];\n        }\n    }\n    // Re-index everything\n    reIndex(lis2);\n    lis = lis2;\n    draw();\n//    document.getElementById('hiddenOrder').value = getOrder();\n    document.getElementsBySelector('input[name=order_]')[0].value = getOrder();\n}\n\nfunction reIndex(lis) {\n    for (var i = 0; i < lis.length; i++) {\n        lis[i].index = i;\n    }\n}\n\nfunction draw() {\n    for (var i = 0; i < lis.length; i++) {\n        var li = lis[i];\n        li.index = i;\n        li.style.position = 'absolute';\n        li.style.left = (10 + left) + 'px';\n        li.style.top = (10 + top + (i * height)) + 'px';\n    }\n}\n\nfunction getOrder() {\n    var order = new Array(lis.length);\n    for (var i = 0; i < lis.length; i++) {\n        order[i] = lis[i].id.substring(1, 100);\n    }\n    return order.join(',');\n}\n\nfunction setOrder(id_list) {\n    /* Set the current order to match the lsit of IDs */\n    var temp_lis = new Array();\n    for (var i = 0; i < id_list.length; i++) {\n        var id = 'p' + id_list[i];\n        temp_lis[temp_lis.length] = document.getElementById(id);\n    }\n    reIndex(temp_lis);\n    lis = temp_lis;\n    draw();\n}\n\nfunction addEvent(elm, evType, fn, useCapture)\n// addEvent and removeEvent\n// cross-browser event handling for IE5+,  NS6 and Mozilla\n// By Scott Andrew\n{\n  if (elm.addEventListener){\n    elm.addEventListener(evType, fn, useCapture);\n    return true;\n  } else if (elm.attachEvent){\n    var r = elm.attachEvent(\"on\"+evType, fn);\n    return r;\n  } else {\n    elm['on'+evType] = fn;\n  }\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/calendar.js",
    "content": "/*\ncalendar.js - Calendar functions by Adrian Holovaty\n*/\n\nfunction removeChildren(a) { // \"a\" is reference to an object\n    while (a.hasChildNodes()) a.removeChild(a.lastChild);\n}\n\n// quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]);\nfunction quickElement() {\n    var obj = document.createElement(arguments[0]);\n    if (arguments[2] != '' && arguments[2] != null) {\n        var textNode = document.createTextNode(arguments[2]);\n        obj.appendChild(textNode);\n    }\n    var len = arguments.length;\n    for (var i = 3; i < len; i += 2) {\n        obj.setAttribute(arguments[i], arguments[i+1]);\n    }\n    arguments[1].appendChild(obj);\n    return obj;\n}\n\n// CalendarNamespace -- Provides a collection of HTML calendar-related helper functions\nvar CalendarNamespace = {\n    monthsOfYear: gettext('January February March April May June July August September October November December').split(' '),\n    daysOfWeek: gettext('S M T W T F S').split(' '),\n    firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')),\n    isLeapYear: function(year) {\n        return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0));\n    },\n    getDaysInMonth: function(month,year) {\n        var days;\n        if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) {\n            days = 31;\n        }\n        else if (month==4 || month==6 || month==9 || month==11) {\n            days = 30;\n        }\n        else if (month==2 && CalendarNamespace.isLeapYear(year)) {\n            days = 29;\n        }\n        else {\n            days = 28;\n        }\n        return days;\n    },\n    draw: function(month, year, div_id, callback) { // month = 1-12, year = 1-9999\n        var today = new Date();\n        var todayDay = today.getDate();\n        var todayMonth = today.getMonth()+1;\n        var todayYear = today.getFullYear();\n        var todayClass = '';\n\n        month = parseInt(month);\n        year = parseInt(year);\n        var calDiv = document.getElementById(div_id);\n        removeChildren(calDiv);\n        var calTable = document.createElement('table');\n        quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month-1] + ' ' + year);\n        var tableBody = quickElement('tbody', calTable);\n\n        // Draw days-of-week header\n        var tableRow = quickElement('tr', tableBody);\n        for (var i = 0; i < 7; i++) {\n            quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]);\n        }\n\n        var startingPos = new Date(year, month-1, 1 - CalendarNamespace.firstDayOfWeek).getDay();\n        var days = CalendarNamespace.getDaysInMonth(month, year);\n\n        // Draw blanks before first of month\n        tableRow = quickElement('tr', tableBody);\n        for (var i = 0; i < startingPos; i++) {\n            var _cell = quickElement('td', tableRow, ' ');\n            _cell.style.backgroundColor = '#f3f3f3';\n        }\n\n        // Draw days of month\n        var currentDay = 1;\n        for (var i = startingPos; currentDay <= days; i++) {\n            if (i%7 == 0 && currentDay != 1) {\n                tableRow = quickElement('tr', tableBody);\n            }\n            if ((currentDay==todayDay) && (month==todayMonth) && (year==todayYear)) {\n                todayClass='today';\n            } else {\n                todayClass='';\n            }\n            var cell = quickElement('td', tableRow, '', 'class', todayClass);\n\n            quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));');\n            currentDay++;\n        }\n\n        // Draw blanks after end of month (optional, but makes for valid code)\n        while (tableRow.childNodes.length < 7) {\n            var _cell = quickElement('td', tableRow, ' ');\n            _cell.style.backgroundColor = '#f3f3f3';\n        }\n\n        calDiv.appendChild(calTable);\n    }\n}\n\n// Calendar -- A calendar instance\nfunction Calendar(div_id, callback) {\n    // div_id (string) is the ID of the element in which the calendar will\n    //     be displayed\n    // callback (string) is the name of a JavaScript function that will be\n    //     called with the parameters (year, month, day) when a day in the\n    //     calendar is clicked\n    this.div_id = div_id;\n    this.callback = callback;\n    this.today = new Date();\n    this.currentMonth = this.today.getMonth() + 1;\n    this.currentYear = this.today.getFullYear();\n}\nCalendar.prototype = {\n    drawCurrent: function() {\n        CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback);\n    },\n    drawDate: function(month, year) {\n        this.currentMonth = month;\n        this.currentYear = year;\n        this.drawCurrent();\n    },\n    drawPreviousMonth: function() {\n        if (this.currentMonth == 1) {\n            this.currentMonth = 12;\n            this.currentYear--;\n        }\n        else {\n            this.currentMonth--;\n        }\n        this.drawCurrent();\n    },\n    drawNextMonth: function() {\n        if (this.currentMonth == 12) {\n            this.currentMonth = 1;\n            this.currentYear++;\n        }\n        else {\n            this.currentMonth++;\n        }\n        this.drawCurrent();\n    },\n    drawPreviousYear: function() {\n        this.currentYear--;\n        this.drawCurrent();\n    },\n    drawNextYear: function() {\n        this.currentYear++;\n        this.drawCurrent();\n    }\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/collapse.js",
    "content": "(function($) {\n\t$(document).ready(function() {\n\t\t// Add anchor tag for Show/Hide link\n\t\t$(\"fieldset.collapse\").each(function(i, elem) {\n\t\t\t// Don't hide if fields in this fieldset have errors\n\t\t\tif ( $(elem).find(\"div.errors\").length == 0 ) {\n\t\t\t\t$(elem).addClass(\"collapsed\");\n\t\t\t\t$(elem).find(\"h2\").first().append(' (<a id=\"fieldsetcollapser' +\n\t\t\t\t\ti +'\" class=\"collapse-toggle\" href=\"#\">' + gettext(\"Show\") +\n\t\t\t\t\t'</a>)');\n\t\t\t}\n\t\t});\n\t\t// Add toggle to anchor tag\n\t\t$(\"fieldset.collapse a.collapse-toggle\").toggle(\n\t\t\tfunction() { // Show\n\t\t\t\t$(this).text(gettext(\"Hide\"));\n\t\t\t\t$(this).closest(\"fieldset\").removeClass(\"collapsed\");\n\t\t\t\treturn false;\n\t\t\t},\n\t\t\tfunction() { // Hide\n\t\t\t\t$(this).text(gettext(\"Show\"));\n\t\t\t\t$(this).closest(\"fieldset\").addClass(\"collapsed\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t);\n\t});\n})(django.jQuery);\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/compress.py",
    "content": "#!/usr/bin/env python\nimport os\nimport optparse\nimport subprocess\nimport sys\n\nhere = os.path.dirname(__file__)\n\ndef main():\n    usage = \"usage: %prog [file1..fileN]\"\n    description = \"\"\"With no file paths given this script will automatically\ncompress all jQuery-based files of the admin app. Requires the Google Closure\nCompiler library and Java version 6 or later.\"\"\"\n    parser = optparse.OptionParser(usage, description=description)\n    parser.add_option(\"-c\", dest=\"compiler\", default=\"~/bin/compiler.jar\",\n                      help=\"path to Closure Compiler jar file\")\n    parser.add_option(\"-v\", \"--verbose\",\n                      action=\"store_true\", dest=\"verbose\")\n    parser.add_option(\"-q\", \"--quiet\",\n                      action=\"store_false\", dest=\"verbose\")\n    (options, args) = parser.parse_args()\n\n    compiler = os.path.expanduser(options.compiler)\n    if not os.path.exists(compiler):\n        sys.exit(\"Google Closure compiler jar file %s not found. Please use the -c option to specify the path.\" % compiler)\n\n    if not args:\n        if options.verbose:\n            sys.stdout.write(\"No filenames given; defaulting to admin scripts\\n\")\n        args = [os.path.join(here, f) for f in [\n            \"actions.js\", \"collapse.js\", \"inlines.js\", \"prepopulate.js\"]]\n\n    for arg in args:\n        if not arg.endswith(\".js\"):\n            arg = arg + \".js\"\n        to_compress = os.path.expanduser(arg)\n        if os.path.exists(to_compress):\n            to_compress_min = \"%s.min.js\" % \"\".join(arg.rsplit(\".js\"))\n            cmd = \"java -jar %s --js %s --js_output_file %s\" % (compiler, to_compress, to_compress_min)\n            if options.verbose:\n                sys.stdout.write(\"Running: %s\\n\" % cmd)\n            subprocess.call(cmd.split())\n        else:\n            sys.stdout.write(\"File %s not found. Sure it exists?\\n\" % to_compress)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/core.js",
    "content": "// Core javascript helper functions\n\n// basic browser identification & version\nvar isOpera = (navigator.userAgent.indexOf(\"Opera\")>=0) && parseFloat(navigator.appVersion);\nvar isIE = ((document.all) && (!isOpera)) && parseFloat(navigator.appVersion.split(\"MSIE \")[1].split(\";\")[0]);\n\n// Cross-browser event handlers.\nfunction addEvent(obj, evType, fn) {\n    if (obj.addEventListener) {\n        obj.addEventListener(evType, fn, false);\n        return true;\n    } else if (obj.attachEvent) {\n        var r = obj.attachEvent(\"on\" + evType, fn);\n        return r;\n    } else {\n        return false;\n    }\n}\n\nfunction removeEvent(obj, evType, fn) {\n    if (obj.removeEventListener) {\n        obj.removeEventListener(evType, fn, false);\n        return true;\n    } else if (obj.detachEvent) {\n        obj.detachEvent(\"on\" + evType, fn);\n        return true;\n    } else {\n        return false;\n    }\n}\n\n// quickElement(tagType, parentReference, textInChildNode, [, attribute, attributeValue ...]);\nfunction quickElement() {\n    var obj = document.createElement(arguments[0]);\n    if (arguments[2] != '' && arguments[2] != null) {\n        var textNode = document.createTextNode(arguments[2]);\n        obj.appendChild(textNode);\n    }\n    var len = arguments.length;\n    for (var i = 3; i < len; i += 2) {\n        obj.setAttribute(arguments[i], arguments[i+1]);\n    }\n    arguments[1].appendChild(obj);\n    return obj;\n}\n\n// ----------------------------------------------------------------------------\n// Cross-browser xmlhttp object\n// from http://jibbering.com/2002/4/httprequest.html\n// ----------------------------------------------------------------------------\nvar xmlhttp;\n/*@cc_on @*/\n/*@if (@_jscript_version >= 5)\n    try {\n        xmlhttp = new ActiveXObject(\"Msxml2.XMLHTTP\");\n    } catch (e) {\n        try {\n            xmlhttp = new ActiveXObject(\"Microsoft.XMLHTTP\");\n        } catch (E) {\n            xmlhttp = false;\n        }\n    }\n@else\n    xmlhttp = false;\n@end @*/\nif (!xmlhttp && typeof XMLHttpRequest != 'undefined') {\n  xmlhttp = new XMLHttpRequest();\n}\n\n// ----------------------------------------------------------------------------\n// Find-position functions by PPK\n// See http://www.quirksmode.org/js/findpos.html\n// ----------------------------------------------------------------------------\nfunction findPosX(obj) {\n    var curleft = 0;\n    if (obj.offsetParent) {\n        while (obj.offsetParent) {\n            curleft += obj.offsetLeft - ((isOpera) ? 0 : obj.scrollLeft);\n            obj = obj.offsetParent;\n        }\n        // IE offsetParent does not include the top-level\n        if (isIE && obj.parentElement){\n            curleft += obj.offsetLeft - obj.scrollLeft;\n        }\n    } else if (obj.x) {\n        curleft += obj.x;\n    }\n    return curleft;\n}\n\nfunction findPosY(obj) {\n    var curtop = 0;\n    if (obj.offsetParent) {\n        while (obj.offsetParent) {\n            curtop += obj.offsetTop - ((isOpera) ? 0 : obj.scrollTop);\n            obj = obj.offsetParent;\n        }\n        // IE offsetParent does not include the top-level\n        if (isIE && obj.parentElement){\n            curtop += obj.offsetTop - obj.scrollTop;\n        }\n    } else if (obj.y) {\n        curtop += obj.y;\n    }\n    return curtop;\n}\n\n//-----------------------------------------------------------------------------\n// Date object extensions\n// ----------------------------------------------------------------------------\nDate.prototype.getCorrectYear = function() {\n    // Date.getYear() is unreliable --\n    // see http://www.quirksmode.org/js/introdate.html#year\n    var y = this.getYear() % 100;\n    return (y < 38) ? y + 2000 : y + 1900;\n}\n\nDate.prototype.getTwelveHours = function() {\n    hours = this.getHours();\n    if (hours == 0) {\n        return 12;\n    }\n    else {\n        return hours <= 12 ? hours : hours-12\n    }\n}\n\nDate.prototype.getTwoDigitMonth = function() {\n    return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1);\n}\n\nDate.prototype.getTwoDigitDate = function() {\n    return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate();\n}\n\nDate.prototype.getTwoDigitTwelveHour = function() {\n    return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours();\n}\n\nDate.prototype.getTwoDigitHour = function() {\n    return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours();\n}\n\nDate.prototype.getTwoDigitMinute = function() {\n    return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes();\n}\n\nDate.prototype.getTwoDigitSecond = function() {\n    return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds();\n}\n\nDate.prototype.getISODate = function() {\n    return this.getCorrectYear() + '-' + this.getTwoDigitMonth() + '-' + this.getTwoDigitDate();\n}\n\nDate.prototype.getHourMinute = function() {\n    return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute();\n}\n\nDate.prototype.getHourMinuteSecond = function() {\n    return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond();\n}\n\nDate.prototype.strftime = function(format) {\n    var fields = {\n        c: this.toString(),\n        d: this.getTwoDigitDate(),\n        H: this.getTwoDigitHour(),\n        I: this.getTwoDigitTwelveHour(),\n        m: this.getTwoDigitMonth(),\n        M: this.getTwoDigitMinute(),\n        p: (this.getHours() >= 12) ? 'PM' : 'AM',\n        S: this.getTwoDigitSecond(),\n        w: '0' + this.getDay(),\n        x: this.toLocaleDateString(),\n        X: this.toLocaleTimeString(),\n        y: ('' + this.getFullYear()).substr(2, 4),\n        Y: '' + this.getFullYear(),\n        '%' : '%'\n    };\n    var result = '', i = 0;\n    while (i < format.length) {\n        if (format.charAt(i) === '%') {\n            result = result + fields[format.charAt(i + 1)];\n            ++i;\n        }\n        else {\n            result = result + format.charAt(i);\n        }\n        ++i;\n    }\n    return result;\n}\n\n// ----------------------------------------------------------------------------\n// String object extensions\n// ----------------------------------------------------------------------------\nString.prototype.pad_left = function(pad_length, pad_string) {\n    var new_string = this;\n    for (var i = 0; new_string.length < pad_length; i++) {\n        new_string = pad_string + new_string;\n    }\n    return new_string;\n}\n\n// ----------------------------------------------------------------------------\n// Get the computed style for and element\n// ----------------------------------------------------------------------------\nfunction getStyle(oElm, strCssRule){\n    var strValue = \"\";\n    if(document.defaultView && document.defaultView.getComputedStyle){\n        strValue = document.defaultView.getComputedStyle(oElm, \"\").getPropertyValue(strCssRule);\n    }\n    else if(oElm.currentStyle){\n        strCssRule = strCssRule.replace(/\\-(\\w)/g, function (strMatch, p1){\n            return p1.toUpperCase();\n        });\n        strValue = oElm.currentStyle[strCssRule];\n    }\n    return strValue;\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/dateparse.js",
    "content": "/* 'Magic' date parsing, by Simon Willison (6th October 2003)\n   http://simon.incutio.com/archive/2003/10/06/betterDateInput\n   Adapted for 6newslawrence.com, 28th January 2004\n*/\n\n/* Finds the index of the first occurence of item in the array, or -1 if not found */\nif (typeof Array.prototype.indexOf == 'undefined') {\n    Array.prototype.indexOf = function(item) {\n        var len = this.length;\n        for (var i = 0; i < len; i++) {\n            if (this[i] == item) {\n                return i;\n            }\n        }\n        return -1;\n    };\n}\n/* Returns an array of items judged 'true' by the passed in test function */\nif (typeof Array.prototype.filter == 'undefined') {\n    Array.prototype.filter = function(test) {\n        var matches = [];\n        var len = this.length;\n        for (var i = 0; i < len; i++) {\n            if (test(this[i])) {\n                matches[matches.length] = this[i];\n            }\n        }\n        return matches;\n    };\n}\n\nvar monthNames = gettext(\"January February March April May June July August September October November December\").split(\" \");\nvar weekdayNames = gettext(\"Sunday Monday Tuesday Wednesday Thursday Friday Saturday\").split(\" \");\n\n/* Takes a string, returns the index of the month matching that string, throws\n   an error if 0 or more than 1 matches\n*/\nfunction parseMonth(month) {\n    var matches = monthNames.filter(function(item) { \n        return new RegExp(\"^\" + month, \"i\").test(item);\n    });\n    if (matches.length == 0) {\n        throw new Error(\"Invalid month string\");\n    }\n    if (matches.length > 1) {\n        throw new Error(\"Ambiguous month\");\n    }\n    return monthNames.indexOf(matches[0]);\n}\n/* Same as parseMonth but for days of the week */\nfunction parseWeekday(weekday) {\n    var matches = weekdayNames.filter(function(item) {\n        return new RegExp(\"^\" + weekday, \"i\").test(item);\n    });\n    if (matches.length == 0) {\n        throw new Error(\"Invalid day string\");\n    }\n    if (matches.length > 1) {\n        throw new Error(\"Ambiguous weekday\");\n    }\n    return weekdayNames.indexOf(matches[0]);\n}\n\n/* Array of objects, each has 're', a regular expression and 'handler', a \n   function for creating a date from something that matches the regular \n   expression. Handlers may throw errors if string is unparseable. \n*/\nvar dateParsePatterns = [\n    // Today\n    {   re: /^tod/i,\n        handler: function() { \n            return new Date();\n        } \n    },\n    // Tomorrow\n    {   re: /^tom/i,\n        handler: function() {\n            var d = new Date(); \n            d.setDate(d.getDate() + 1); \n            return d;\n        }\n    },\n    // Yesterday\n    {   re: /^yes/i,\n        handler: function() {\n            var d = new Date();\n            d.setDate(d.getDate() - 1);\n            return d;\n        }\n    },\n    // 4th\n    {   re: /^(\\d{1,2})(st|nd|rd|th)?$/i, \n        handler: function(bits) {\n            var d = new Date();\n            d.setDate(parseInt(bits[1], 10));\n            return d;\n        }\n    },\n    // 4th Jan\n    {   re: /^(\\d{1,2})(?:st|nd|rd|th)? (\\w+)$/i, \n        handler: function(bits) {\n            var d = new Date();\n            d.setDate(parseInt(bits[1], 10));\n            d.setMonth(parseMonth(bits[2]));\n            return d;\n        }\n    },\n    // 4th Jan 2003\n    {   re: /^(\\d{1,2})(?:st|nd|rd|th)? (\\w+),? (\\d{4})$/i,\n        handler: function(bits) {\n            var d = new Date();\n            d.setDate(parseInt(bits[1], 10));\n            d.setMonth(parseMonth(bits[2]));\n            d.setYear(bits[3]);\n            return d;\n        }\n    },\n    // Jan 4th\n    {   re: /^(\\w+) (\\d{1,2})(?:st|nd|rd|th)?$/i, \n        handler: function(bits) {\n            var d = new Date();\n            d.setDate(parseInt(bits[2], 10));\n            d.setMonth(parseMonth(bits[1]));\n            return d;\n        }\n    },\n    // Jan 4th 2003\n    {   re: /^(\\w+) (\\d{1,2})(?:st|nd|rd|th)?,? (\\d{4})$/i,\n        handler: function(bits) {\n            var d = new Date();\n            d.setDate(parseInt(bits[2], 10));\n            d.setMonth(parseMonth(bits[1]));\n            d.setYear(bits[3]);\n            return d;\n        }\n    },\n    // next Tuesday - this is suspect due to weird meaning of \"next\"\n    {   re: /^next (\\w+)$/i,\n        handler: function(bits) {\n            var d = new Date();\n            var day = d.getDay();\n            var newDay = parseWeekday(bits[1]);\n            var addDays = newDay - day;\n            if (newDay <= day) {\n                addDays += 7;\n            }\n            d.setDate(d.getDate() + addDays);\n            return d;\n        }\n    },\n    // last Tuesday\n    {   re: /^last (\\w+)$/i,\n        handler: function(bits) {\n            throw new Error(\"Not yet implemented\");\n        }\n    },\n    // mm/dd/yyyy (American style)\n    {   re: /(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})/,\n        handler: function(bits) {\n            var d = new Date();\n            d.setYear(bits[3]);\n            d.setDate(parseInt(bits[2], 10));\n            d.setMonth(parseInt(bits[1], 10) - 1); // Because months indexed from 0\n            return d;\n        }\n    },\n    // yyyy-mm-dd (ISO style)\n    {   re: /(\\d{4})-(\\d{1,2})-(\\d{1,2})/,\n        handler: function(bits) {\n            var d = new Date();\n            d.setYear(parseInt(bits[1]));\n            d.setMonth(parseInt(bits[2], 10) - 1);\n            d.setDate(parseInt(bits[3], 10));\n            return d;\n        }\n    },\n];\n\nfunction parseDateString(s) {\n    for (var i = 0; i < dateParsePatterns.length; i++) {\n        var re = dateParsePatterns[i].re;\n        var handler = dateParsePatterns[i].handler;\n        var bits = re.exec(s);\n        if (bits) {\n            return handler(bits);\n        }\n    }\n    throw new Error(\"Invalid date string\");\n}\n\nfunction fmt00(x) {\n    // fmt00: Tags leading zero onto numbers 0 - 9.\n    // Particularly useful for displaying results from Date methods.\n    //\n    if (Math.abs(parseInt(x)) < 10){\n        x = \"0\"+ Math.abs(x);\n    }\n    return x;\n}\n\nfunction parseDateStringISO(s) {\n    try {\n        var d = parseDateString(s);\n        return d.getFullYear() + '-' + (fmt00(d.getMonth() + 1)) + '-' + fmt00(d.getDate())\n    }\n    catch (e) { return s; }\n}\nfunction magicDate(input) {\n    var messagespan = input.id + 'Msg';\n    try {\n        var d = parseDateString(input.value);\n        input.value = d.getFullYear() + '-' + (fmt00(d.getMonth() + 1)) + '-' + \n            fmt00(d.getDate());\n        input.className = '';\n        // Human readable date\n        if (document.getElementById(messagespan)) {\n            document.getElementById(messagespan).firstChild.nodeValue = d.toDateString();\n            document.getElementById(messagespan).className = 'normal';\n        }\n    }\n    catch (e) {\n        input.className = 'error';\n        var message = e.message;\n        // Fix for IE6 bug\n        if (message.indexOf('is null or not an object') > -1) {\n            message = 'Invalid date string';\n        }\n        if (document.getElementById(messagespan)) {\n            document.getElementById(messagespan).firstChild.nodeValue = message;\n            document.getElementById(messagespan).className = 'error';\n        }\n    }\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/getElementsBySelector.js",
    "content": "/* document.getElementsBySelector(selector)\n   - returns an array of element objects from the current document\n     matching the CSS selector. Selectors can contain element names, \n     class names and ids and can be nested. For example:\n     \n       elements = document.getElementsBySelect('div#main p a.external')\n     \n     Will return an array of all 'a' elements with 'external' in their \n     class attribute that are contained inside 'p' elements that are \n     contained inside the 'div' element which has id=\"main\"\n\n   New in version 0.4: Support for CSS2 and CSS3 attribute selectors:\n   See http://www.w3.org/TR/css3-selectors/#attribute-selectors\n\n   Version 0.4 - Simon Willison, March 25th 2003\n   -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows\n   -- Opera 7 fails \n*/\n\nfunction getAllChildren(e) {\n  // Returns all children of element. Workaround required for IE5/Windows. Ugh.\n  return e.all ? e.all : e.getElementsByTagName('*');\n}\n\ndocument.getElementsBySelector = function(selector) {\n  // Attempt to fail gracefully in lesser browsers\n  if (!document.getElementsByTagName) {\n    return new Array();\n  }\n  // Split selector in to tokens\n  var tokens = selector.split(' ');\n  var currentContext = new Array(document);\n  for (var i = 0; i < tokens.length; i++) {\n    token = tokens[i].replace(/^\\s+/,'').replace(/\\s+$/,'');;\n    if (token.indexOf('#') > -1) {\n      // Token is an ID selector\n      var bits = token.split('#');\n      var tagName = bits[0];\n      var id = bits[1];\n      var element = document.getElementById(id);\n      if (!element || (tagName && element.nodeName.toLowerCase() != tagName)) {\n        // ID not found or tag with that ID not found, return false.\n        return new Array();\n      }\n      // Set currentContext to contain just this element\n      currentContext = new Array(element);\n      continue; // Skip to next token\n    }\n    if (token.indexOf('.') > -1) {\n      // Token contains a class selector\n      var bits = token.split('.');\n      var tagName = bits[0];\n      var className = bits[1];\n      if (!tagName) {\n        tagName = '*';\n      }\n      // Get elements matching tag, filter them for class selector\n      var found = new Array;\n      var foundCount = 0;\n      for (var h = 0; h < currentContext.length; h++) {\n        var elements;\n        if (tagName == '*') {\n            elements = getAllChildren(currentContext[h]);\n        } else {\n            try {\n                elements = currentContext[h].getElementsByTagName(tagName);\n            }\n            catch(e) {\n                elements = [];\n            }\n        }\n        for (var j = 0; j < elements.length; j++) {\n          found[foundCount++] = elements[j];\n        }\n      }\n      currentContext = new Array;\n      var currentContextIndex = 0;\n      for (var k = 0; k < found.length; k++) {\n        if (found[k].className && found[k].className.match(new RegExp('\\\\b'+className+'\\\\b'))) {\n          currentContext[currentContextIndex++] = found[k];\n        }\n      }\n      continue; // Skip to next token\n    }\n    // Code to deal with attribute selectors\n    if (token.match(/^(\\w*)\\[(\\w+)([=~\\|\\^\\$\\*]?)=?\"?([^\\]\"]*)\"?\\]$/)) {\n      var tagName = RegExp.$1;\n      var attrName = RegExp.$2;\n      var attrOperator = RegExp.$3;\n      var attrValue = RegExp.$4;\n      if (!tagName) {\n        tagName = '*';\n      }\n      // Grab all of the tagName elements within current context\n      var found = new Array;\n      var foundCount = 0;\n      for (var h = 0; h < currentContext.length; h++) {\n        var elements;\n        if (tagName == '*') {\n            elements = getAllChildren(currentContext[h]);\n        } else {\n            elements = currentContext[h].getElementsByTagName(tagName);\n        }\n        for (var j = 0; j < elements.length; j++) {\n          found[foundCount++] = elements[j];\n        }\n      }\n      currentContext = new Array;\n      var currentContextIndex = 0;\n      var checkFunction; // This function will be used to filter the elements\n      switch (attrOperator) {\n        case '=': // Equality\n          checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };\n          break;\n        case '~': // Match one of space seperated words \n          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\\\b'+attrValue+'\\\\b'))); };\n          break;\n        case '|': // Match start with value followed by optional hyphen\n          checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };\n          break;\n        case '^': // Match starts with value\n          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };\n          break;\n        case '$': // Match ends with value - fails with \"Warning\" in Opera 7\n          checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };\n          break;\n        case '*': // Match ends with value\n          checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };\n          break;\n        default :\n          // Just test for existence of attribute\n          checkFunction = function(e) { return e.getAttribute(attrName); };\n      }\n      currentContext = new Array;\n      var currentContextIndex = 0;\n      for (var k = 0; k < found.length; k++) {\n        if (checkFunction(found[k])) {\n          currentContext[currentContextIndex++] = found[k];\n        }\n      }\n      // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);\n      continue; // Skip to next token\n    }\n    // If we get here, token is JUST an element (not a class or ID selector)\n    tagName = token;\n    var found = new Array;\n    var foundCount = 0;\n    for (var h = 0; h < currentContext.length; h++) {\n      var elements = currentContext[h].getElementsByTagName(tagName);\n      for (var j = 0; j < elements.length; j++) {\n        found[foundCount++] = elements[j];\n      }\n    }\n    currentContext = found;\n  }\n  return currentContext;\n}\n\n/* That revolting regular expression explained \n/^(\\w+)\\[(\\w+)([=~\\|\\^\\$\\*]?)=?\"?([^\\]\"]*)\"?\\]$/\n  \\---/  \\---/\\-------------/    \\-------/\n    |      |         |               |\n    |      |         |           The value\n    |      |    ~,|,^,$,* or =\n    |   Attribute \n   Tag\n*/\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/inlines.js",
    "content": "/**\n * Django admin inlines\n *\n * Based on jQuery Formset 1.1\n * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)\n * @requires jQuery 1.2.6 or later\n *\n * Copyright (c) 2009, Stanislaus Madueke\n * All rights reserved.\n *\n * Spiced up with Code from Zain Memon's GSoC project 2009\n * and modified for Django by Jannis Leidel\n *\n * Licensed under the New BSD License\n * See: http://www.opensource.org/licenses/bsd-license.php\n */\n(function($) {\n\t$.fn.formset = function(opts) {\n\t\tvar options = $.extend({}, $.fn.formset.defaults, opts);\n\t\tvar updateElementIndex = function(el, prefix, ndx) {\n\t\t\tvar id_regex = new RegExp(\"(\" + prefix + \"-(\\\\d+|__prefix__))\");\n\t\t\tvar replacement = prefix + \"-\" + ndx;\n\t\t\tif ($(el).attr(\"for\")) {\n\t\t\t\t$(el).attr(\"for\", $(el).attr(\"for\").replace(id_regex, replacement));\n\t\t\t}\n\t\t\tif (el.id) {\n\t\t\t\tel.id = el.id.replace(id_regex, replacement);\n\t\t\t}\n\t\t\tif (el.name) {\n\t\t\t\tel.name = el.name.replace(id_regex, replacement);\n\t\t\t}\n\t\t};\n\t\tvar totalForms = $(\"#id_\" + options.prefix + \"-TOTAL_FORMS\").attr(\"autocomplete\", \"off\");\n\t\tvar nextIndex = parseInt(totalForms.val());\n\t\tvar maxForms = $(\"#id_\" + options.prefix + \"-MAX_NUM_FORMS\").attr(\"autocomplete\", \"off\");\n\t\t// only show the add button if we are allowed to add more items,\n        // note that max_num = None translates to a blank string.\n\t\tvar showAddButton = maxForms.val() == '' || (maxForms.val()-totalForms.val()) > 0;\n\t\t$(this).each(function(i) {\n\t\t\t$(this).not(\".\" + options.emptyCssClass).addClass(options.formCssClass);\n\t\t});\n\t\tif ($(this).length && showAddButton) {\n\t\t\tvar addButton;\n\t\t\tif ($(this).attr(\"tagName\") == \"TR\") {\n\t\t\t\t// If forms are laid out as table rows, insert the\n\t\t\t\t// \"add\" button in a new table row:\n\t\t\t\tvar numCols = this.eq(0).children().length;\n\t\t\t\t$(this).parent().append('<tr class=\"' + options.addCssClass + '\"><td colspan=\"' + numCols + '\"><a href=\"javascript:void(0)\">' + options.addText + \"</a></tr>\");\n\t\t\t\taddButton = $(this).parent().find(\"tr:last a\");\n\t\t\t} else {\n\t\t\t\t// Otherwise, insert it immediately after the last form:\n\t\t\t\t$(this).filter(\":last\").after('<div class=\"' + options.addCssClass + '\"><a href=\"javascript:void(0)\">' + options.addText + \"</a></div>\");\n\t\t\t\taddButton = $(this).filter(\":last\").next().find(\"a\");\n\t\t\t}\n\t\t\taddButton.click(function() {\n\t\t\t\tvar totalForms = $(\"#id_\" + options.prefix + \"-TOTAL_FORMS\");\n\t\t\t\tvar template = $(\"#\" + options.prefix + \"-empty\");\n\t\t\t\tvar row = template.clone(true);\n\t\t\t\trow.removeClass(options.emptyCssClass)\n\t\t\t\t    .addClass(options.formCssClass)\n\t\t\t\t    .attr(\"id\", options.prefix + \"-\" + nextIndex);\n\t\t\t\tif (row.is(\"tr\")) {\n\t\t\t\t\t// If the forms are laid out in table rows, insert\n\t\t\t\t\t// the remove button into the last table cell:\n\t\t\t\t\trow.children(\":last\").append('<div><a class=\"' + options.deleteCssClass +'\" href=\"javascript:void(0)\">' + options.deleteText + \"</a></div>\");\n\t\t\t\t} else if (row.is(\"ul\") || row.is(\"ol\")) {\n\t\t\t\t\t// If they're laid out as an ordered/unordered list,\n\t\t\t\t\t// insert an <li> after the last list item:\n\t\t\t\t\trow.append('<li><a class=\"' + options.deleteCssClass +'\" href=\"javascript:void(0)\">' + options.deleteText + \"</a></li>\");\n\t\t\t\t} else {\n\t\t\t\t\t// Otherwise, just insert the remove button as the\n\t\t\t\t\t// last child element of the form's container:\n\t\t\t\t\trow.children(\":first\").append('<span><a class=\"' + options.deleteCssClass + '\" href=\"javascript:void(0)\">' + options.deleteText + \"</a></span>\");\n\t\t\t\t}\n\t\t\t\trow.find(\"*\").each(function() {\n\t\t\t\t\tupdateElementIndex(this, options.prefix, totalForms.val());\n\t\t\t\t});\n\t\t\t\t// Insert the new form when it has been fully edited\n\t\t\t\trow.insertBefore($(template));\n\t\t\t\t// Update number of total forms\n\t\t\t\t$(totalForms).val(parseInt(totalForms.val()) + 1);\n\t\t\t\tnextIndex += 1;\n\t\t\t\t// Hide add button in case we've hit the max, except we want to add infinitely\n\t\t\t\tif ((maxForms.val() != '') && (maxForms.val()-totalForms.val()) <= 0) {\n\t\t\t\t\taddButton.parent().hide();\n\t\t\t\t}\n\t\t\t\t// The delete button of each row triggers a bunch of other things\n\t\t\t\trow.find(\"a.\" + options.deleteCssClass).click(function() {\n\t\t\t\t\t// Remove the parent form containing this button:\n\t\t\t\t\tvar row = $(this).parents(\".\" + options.formCssClass);\n\t\t\t\t\trow.remove();\n\t\t\t\t\tnextIndex -= 1;\n\t\t\t\t\t// If a post-delete callback was provided, call it with the deleted form:\n\t\t\t\t\tif (options.removed) {\n\t\t\t\t\t\toptions.removed(row);\n\t\t\t\t\t}\n\t\t\t\t\t// Update the TOTAL_FORMS form count.\n\t\t\t\t\tvar forms = $(\".\" + options.formCssClass);\n\t\t\t\t\t$(\"#id_\" + options.prefix + \"-TOTAL_FORMS\").val(forms.length);\n\t\t\t\t\t// Show add button again once we drop below max\n\t\t\t\t\tif ((maxForms.val() == '') || (maxForms.val()-forms.length) > 0) {\n\t\t\t\t\t\taddButton.parent().show();\n\t\t\t\t\t}\n\t\t\t\t\t// Also, update names and ids for all remaining form controls\n\t\t\t\t\t// so they remain in sequence:\n\t\t\t\t\tfor (var i=0, formCount=forms.length; i<formCount; i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tupdateElementIndex($(forms).get(i), options.prefix, i);\n\t\t\t\t\t\t$(forms.get(i)).find(\"*\").each(function() {\n\t\t\t\t\t\t\tupdateElementIndex(this, options.prefix, i);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t});\n\t\t\t\t// If a post-add callback was supplied, call it with the added form:\n\t\t\t\tif (options.added) {\n\t\t\t\t\toptions.added(row);\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t});\n\t\t}\n\t\treturn this;\n\t}\n\t/* Setup plugin defaults */\n\t$.fn.formset.defaults = {\n\t\tprefix: \"form\",\t\t\t\t\t// The form prefix for your django formset\n\t\taddText: \"add another\",\t\t\t// Text for the add link\n\t\tdeleteText: \"remove\",\t\t\t// Text for the delete link\n\t\taddCssClass: \"add-row\",\t\t\t// CSS class applied to the add link\n\t\tdeleteCssClass: \"delete-row\",\t// CSS class applied to the delete link\n\t\temptyCssClass: \"empty-row\",\t\t// CSS class applied to the empty row\n\t\tformCssClass: \"dynamic-form\",\t// CSS class applied to each form in a formset\n\t\tadded: null,\t\t\t\t\t// Function called each time a new form is added\n\t\tremoved: null\t\t\t\t\t// Function called each time a form is deleted\n\t}\n})(django.jQuery);\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/jquery.init.js",
    "content": "// Puts the included jQuery into our own namespace\nvar django = {\n    \"jQuery\": jQuery.noConflict(true)\n};\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/jquery.js",
    "content": "/*!\n * jQuery JavaScript Library v1.4.2\n * http://jquery.com/\n *\n * Copyright 2010, John Resig\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * Includes Sizzle.js\n * http://sizzlejs.com/\n * Copyright 2010, The Dojo Foundation\n * Released under the MIT, BSD, and GPL Licenses.\n *\n * Date: Sat Feb 13 22:33:48 2010 -0500\n */\n(function( window, undefined ) {\n\n// Define a local copy of jQuery\nvar jQuery = function( selector, context ) {\n\t\t// The jQuery object is actually just the init constructor 'enhanced'\n\t\treturn new jQuery.fn.init( selector, context );\n\t},\n\n\t// Map over jQuery in case of overwrite\n\t_jQuery = window.jQuery,\n\n\t// Map over the $ in case of overwrite\n\t_$ = window.$,\n\n\t// Use the correct document accordingly with window argument (sandbox)\n\tdocument = window.document,\n\n\t// A central reference to the root jQuery(document)\n\trootjQuery,\n\n\t// A simple way to check for HTML strings or ID strings\n\t// (both of which we optimize for)\n\tquickExpr = /^[^<]*(<[\\w\\W]+>)[^>]*$|^#([\\w-]+)$/,\n\n\t// Is it a simple selector\n\tisSimple = /^.[^:#\\[\\.,]*$/,\n\n\t// Check if a string has a non-whitespace character in it\n\trnotwhite = /\\S/,\n\n\t// Used for trimming whitespace\n\trtrim = /^(\\s|\\u00A0)+|(\\s|\\u00A0)+$/g,\n\n\t// Match a standalone tag\n\trsingleTag = /^<(\\w+)\\s*\\/?>(?:<\\/\\1>)?$/,\n\n\t// Keep a UserAgent string for use with jQuery.browser\n\tuserAgent = navigator.userAgent,\n\n\t// For matching the engine and version of the browser\n\tbrowserMatch,\n\t\n\t// Has the ready events already been bound?\n\treadyBound = false,\n\t\n\t// The functions to execute on DOM ready\n\treadyList = [],\n\n\t// The ready event handler\n\tDOMContentLoaded,\n\n\t// Save a reference to some core methods\n\ttoString = Object.prototype.toString,\n\thasOwnProperty = Object.prototype.hasOwnProperty,\n\tpush = Array.prototype.push,\n\tslice = Array.prototype.slice,\n\tindexOf = Array.prototype.indexOf;\n\njQuery.fn = jQuery.prototype = {\n\tinit: function( selector, context ) {\n\t\tvar match, elem, ret, doc;\n\n\t\t// Handle $(\"\"), $(null), or $(undefined)\n\t\tif ( !selector ) {\n\t\t\treturn this;\n\t\t}\n\n\t\t// Handle $(DOMElement)\n\t\tif ( selector.nodeType ) {\n\t\t\tthis.context = this[0] = selector;\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\t\t}\n\t\t\n\t\t// The body element only exists once, optimize finding it\n\t\tif ( selector === \"body\" && !context ) {\n\t\t\tthis.context = document;\n\t\t\tthis[0] = document.body;\n\t\t\tthis.selector = \"body\";\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\t\t}\n\n\t\t// Handle HTML strings\n\t\tif ( typeof selector === \"string\" ) {\n\t\t\t// Are we dealing with HTML string or an ID?\n\t\t\tmatch = quickExpr.exec( selector );\n\n\t\t\t// Verify a match, and that no context was specified for #id\n\t\t\tif ( match && (match[1] || !context) ) {\n\n\t\t\t\t// HANDLE: $(html) -> $(array)\n\t\t\t\tif ( match[1] ) {\n\t\t\t\t\tdoc = (context ? context.ownerDocument || context : document);\n\n\t\t\t\t\t// If a single string is passed in and it's a single tag\n\t\t\t\t\t// just do a createElement and skip the rest\n\t\t\t\t\tret = rsingleTag.exec( selector );\n\n\t\t\t\t\tif ( ret ) {\n\t\t\t\t\t\tif ( jQuery.isPlainObject( context ) ) {\n\t\t\t\t\t\t\tselector = [ document.createElement( ret[1] ) ];\n\t\t\t\t\t\t\tjQuery.fn.attr.call( selector, context, true );\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tselector = [ doc.createElement( ret[1] ) ];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tret = buildFragment( [ match[1] ], [ doc ] );\n\t\t\t\t\t\tselector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\treturn jQuery.merge( this, selector );\n\t\t\t\t\t\n\t\t\t\t// HANDLE: $(\"#id\")\n\t\t\t\t} else {\n\t\t\t\t\telem = document.getElementById( match[2] );\n\n\t\t\t\t\tif ( elem ) {\n\t\t\t\t\t\t// Handle the case where IE and Opera return items\n\t\t\t\t\t\t// by name instead of ID\n\t\t\t\t\t\tif ( elem.id !== match[2] ) {\n\t\t\t\t\t\t\treturn rootjQuery.find( selector );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Otherwise, we inject the element directly into the jQuery object\n\t\t\t\t\t\tthis.length = 1;\n\t\t\t\t\t\tthis[0] = elem;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.context = document;\n\t\t\t\t\tthis.selector = selector;\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t// HANDLE: $(\"TAG\")\n\t\t\t} else if ( !context && /^\\w+$/.test( selector ) ) {\n\t\t\t\tthis.selector = selector;\n\t\t\t\tthis.context = document;\n\t\t\t\tselector = document.getElementsByTagName( selector );\n\t\t\t\treturn jQuery.merge( this, selector );\n\n\t\t\t// HANDLE: $(expr, $(...))\n\t\t\t} else if ( !context || context.jquery ) {\n\t\t\t\treturn (context || rootjQuery).find( selector );\n\n\t\t\t// HANDLE: $(expr, context)\n\t\t\t// (which is just equivalent to: $(context).find(expr)\n\t\t\t} else {\n\t\t\t\treturn jQuery( context ).find( selector );\n\t\t\t}\n\n\t\t// HANDLE: $(function)\n\t\t// Shortcut for document ready\n\t\t} else if ( jQuery.isFunction( selector ) ) {\n\t\t\treturn rootjQuery.ready( selector );\n\t\t}\n\n\t\tif (selector.selector !== undefined) {\n\t\t\tthis.selector = selector.selector;\n\t\t\tthis.context = selector.context;\n\t\t}\n\n\t\treturn jQuery.makeArray( selector, this );\n\t},\n\n\t// Start with an empty selector\n\tselector: \"\",\n\n\t// The current version of jQuery being used\n\tjquery: \"1.4.2\",\n\n\t// The default length of a jQuery object is 0\n\tlength: 0,\n\n\t// The number of elements contained in the matched element set\n\tsize: function() {\n\t\treturn this.length;\n\t},\n\n\ttoArray: function() {\n\t\treturn slice.call( this, 0 );\n\t},\n\n\t// Get the Nth element in the matched element set OR\n\t// Get the whole matched element set as a clean array\n\tget: function( num ) {\n\t\treturn num == null ?\n\n\t\t\t// Return a 'clean' array\n\t\t\tthis.toArray() :\n\n\t\t\t// Return just the object\n\t\t\t( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );\n\t},\n\n\t// Take an array of elements and push it onto the stack\n\t// (returning the new matched element set)\n\tpushStack: function( elems, name, selector ) {\n\t\t// Build a new jQuery matched element set\n\t\tvar ret = jQuery();\n\n\t\tif ( jQuery.isArray( elems ) ) {\n\t\t\tpush.apply( ret, elems );\n\t\t\n\t\t} else {\n\t\t\tjQuery.merge( ret, elems );\n\t\t}\n\n\t\t// Add the old object onto the stack (as a reference)\n\t\tret.prevObject = this;\n\n\t\tret.context = this.context;\n\n\t\tif ( name === \"find\" ) {\n\t\t\tret.selector = this.selector + (this.selector ? \" \" : \"\") + selector;\n\t\t} else if ( name ) {\n\t\t\tret.selector = this.selector + \".\" + name + \"(\" + selector + \")\";\n\t\t}\n\n\t\t// Return the newly-formed element set\n\t\treturn ret;\n\t},\n\n\t// Execute a callback for every element in the matched set.\n\t// (You can seed the arguments with an array of args, but this is\n\t// only used internally.)\n\teach: function( callback, args ) {\n\t\treturn jQuery.each( this, callback, args );\n\t},\n\t\n\tready: function( fn ) {\n\t\t// Attach the listeners\n\t\tjQuery.bindReady();\n\n\t\t// If the DOM is already ready\n\t\tif ( jQuery.isReady ) {\n\t\t\t// Execute the function immediately\n\t\t\tfn.call( document, jQuery );\n\n\t\t// Otherwise, remember the function for later\n\t\t} else if ( readyList ) {\n\t\t\t// Add the function to the wait list\n\t\t\treadyList.push( fn );\n\t\t}\n\n\t\treturn this;\n\t},\n\t\n\teq: function( i ) {\n\t\treturn i === -1 ?\n\t\t\tthis.slice( i ) :\n\t\t\tthis.slice( i, +i + 1 );\n\t},\n\n\tfirst: function() {\n\t\treturn this.eq( 0 );\n\t},\n\n\tlast: function() {\n\t\treturn this.eq( -1 );\n\t},\n\n\tslice: function() {\n\t\treturn this.pushStack( slice.apply( this, arguments ),\n\t\t\t\"slice\", slice.call(arguments).join(\",\") );\n\t},\n\n\tmap: function( callback ) {\n\t\treturn this.pushStack( jQuery.map(this, function( elem, i ) {\n\t\t\treturn callback.call( elem, i, elem );\n\t\t}));\n\t},\n\t\n\tend: function() {\n\t\treturn this.prevObject || jQuery(null);\n\t},\n\n\t// For internal use only.\n\t// Behaves like an Array's method, not like a jQuery method.\n\tpush: push,\n\tsort: [].sort,\n\tsplice: [].splice\n};\n\n// Give the init function the jQuery prototype for later instantiation\njQuery.fn.init.prototype = jQuery.fn;\n\njQuery.extend = jQuery.fn.extend = function() {\n\t// copy reference to target object\n\tvar target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;\n\n\t// Handle a deep copy situation\n\tif ( typeof target === \"boolean\" ) {\n\t\tdeep = target;\n\t\ttarget = arguments[1] || {};\n\t\t// skip the boolean and the target\n\t\ti = 2;\n\t}\n\n\t// Handle case when target is a string or something (possible in deep copy)\n\tif ( typeof target !== \"object\" && !jQuery.isFunction(target) ) {\n\t\ttarget = {};\n\t}\n\n\t// extend jQuery itself if only one argument is passed\n\tif ( length === i ) {\n\t\ttarget = this;\n\t\t--i;\n\t}\n\n\tfor ( ; i < length; i++ ) {\n\t\t// Only deal with non-null/undefined values\n\t\tif ( (options = arguments[ i ]) != null ) {\n\t\t\t// Extend the base object\n\t\t\tfor ( name in options ) {\n\t\t\t\tsrc = target[ name ];\n\t\t\t\tcopy = options[ name ];\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif ( target === copy ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Recurse if we're merging object literal values or arrays\n\t\t\t\tif ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {\n\t\t\t\t\tvar clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src\n\t\t\t\t\t\t: jQuery.isArray(copy) ? [] : {};\n\n\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\ttarget[ name ] = jQuery.extend( deep, clone, copy );\n\n\t\t\t\t// Don't bring in undefined values\n\t\t\t\t} else if ( copy !== undefined ) {\n\t\t\t\t\ttarget[ name ] = copy;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\njQuery.extend({\n\tnoConflict: function( deep ) {\n\t\twindow.$ = _$;\n\n\t\tif ( deep ) {\n\t\t\twindow.jQuery = _jQuery;\n\t\t}\n\n\t\treturn jQuery;\n\t},\n\t\n\t// Is the DOM ready to be used? Set to true once it occurs.\n\tisReady: false,\n\t\n\t// Handle when the DOM is ready\n\tready: function() {\n\t\t// Make sure that the DOM is not already loaded\n\t\tif ( !jQuery.isReady ) {\n\t\t\t// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).\n\t\t\tif ( !document.body ) {\n\t\t\t\treturn setTimeout( jQuery.ready, 13 );\n\t\t\t}\n\n\t\t\t// Remember that the DOM is ready\n\t\t\tjQuery.isReady = true;\n\n\t\t\t// If there are functions bound, to execute\n\t\t\tif ( readyList ) {\n\t\t\t\t// Execute all of them\n\t\t\t\tvar fn, i = 0;\n\t\t\t\twhile ( (fn = readyList[ i++ ]) ) {\n\t\t\t\t\tfn.call( document, jQuery );\n\t\t\t\t}\n\n\t\t\t\t// Reset the list of functions\n\t\t\t\treadyList = null;\n\t\t\t}\n\n\t\t\t// Trigger any bound ready events\n\t\t\tif ( jQuery.fn.triggerHandler ) {\n\t\t\t\tjQuery( document ).triggerHandler( \"ready\" );\n\t\t\t}\n\t\t}\n\t},\n\t\n\tbindReady: function() {\n\t\tif ( readyBound ) {\n\t\t\treturn;\n\t\t}\n\n\t\treadyBound = true;\n\n\t\t// Catch cases where $(document).ready() is called after the\n\t\t// browser event has already occurred.\n\t\tif ( document.readyState === \"complete\" ) {\n\t\t\treturn jQuery.ready();\n\t\t}\n\n\t\t// Mozilla, Opera and webkit nightlies currently support this event\n\t\tif ( document.addEventListener ) {\n\t\t\t// Use the handy event callback\n\t\t\tdocument.addEventListener( \"DOMContentLoaded\", DOMContentLoaded, false );\n\t\t\t\n\t\t\t// A fallback to window.onload, that will always work\n\t\t\twindow.addEventListener( \"load\", jQuery.ready, false );\n\n\t\t// If IE event model is used\n\t\t} else if ( document.attachEvent ) {\n\t\t\t// ensure firing before onload,\n\t\t\t// maybe late but safe also for iframes\n\t\t\tdocument.attachEvent(\"onreadystatechange\", DOMContentLoaded);\n\t\t\t\n\t\t\t// A fallback to window.onload, that will always work\n\t\t\twindow.attachEvent( \"onload\", jQuery.ready );\n\n\t\t\t// If IE and not a frame\n\t\t\t// continually check to see if the document is ready\n\t\t\tvar toplevel = false;\n\n\t\t\ttry {\n\t\t\t\ttoplevel = window.frameElement == null;\n\t\t\t} catch(e) {}\n\n\t\t\tif ( document.documentElement.doScroll && toplevel ) {\n\t\t\t\tdoScrollCheck();\n\t\t\t}\n\t\t}\n\t},\n\n\t// See test/unit/core.js for details concerning isFunction.\n\t// Since version 1.3, DOM methods and functions like alert\n\t// aren't supported. They return false on IE (#2968).\n\tisFunction: function( obj ) {\n\t\treturn toString.call(obj) === \"[object Function]\";\n\t},\n\n\tisArray: function( obj ) {\n\t\treturn toString.call(obj) === \"[object Array]\";\n\t},\n\n\tisPlainObject: function( obj ) {\n\t\t// Must be an Object.\n\t\t// Because of IE, we also have to check the presence of the constructor property.\n\t\t// Make sure that DOM nodes and window objects don't pass through, as well\n\t\tif ( !obj || toString.call(obj) !== \"[object Object]\" || obj.nodeType || obj.setInterval ) {\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// Not own constructor property must be Object\n\t\tif ( obj.constructor\n\t\t\t&& !hasOwnProperty.call(obj, \"constructor\")\n\t\t\t&& !hasOwnProperty.call(obj.constructor.prototype, \"isPrototypeOf\") ) {\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\t// Own properties are enumerated firstly, so to speed up,\n\t\t// if last one is own, then all properties are own.\n\t\n\t\tvar key;\n\t\tfor ( key in obj ) {}\n\t\t\n\t\treturn key === undefined || hasOwnProperty.call( obj, key );\n\t},\n\n\tisEmptyObject: function( obj ) {\n\t\tfor ( var name in obj ) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\t\n\terror: function( msg ) {\n\t\tthrow msg;\n\t},\n\t\n\tparseJSON: function( data ) {\n\t\tif ( typeof data !== \"string\" || !data ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Make sure leading/trailing whitespace is removed (IE can't handle it)\n\t\tdata = jQuery.trim( data );\n\t\t\n\t\t// Make sure the incoming data is actual JSON\n\t\t// Logic borrowed from http://json.org/json2.js\n\t\tif ( /^[\\],:{}\\s]*$/.test(data.replace(/\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g, \"@\")\n\t\t\t.replace(/\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g, \"]\")\n\t\t\t.replace(/(?:^|:|,)(?:\\s*\\[)+/g, \"\")) ) {\n\n\t\t\t// Try to use the native JSON parser first\n\t\t\treturn window.JSON && window.JSON.parse ?\n\t\t\t\twindow.JSON.parse( data ) :\n\t\t\t\t(new Function(\"return \" + data))();\n\n\t\t} else {\n\t\t\tjQuery.error( \"Invalid JSON: \" + data );\n\t\t}\n\t},\n\n\tnoop: function() {},\n\n\t// Evalulates a script in a global context\n\tglobalEval: function( data ) {\n\t\tif ( data && rnotwhite.test(data) ) {\n\t\t\t// Inspired by code by Andrea Giammarchi\n\t\t\t// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html\n\t\t\tvar head = document.getElementsByTagName(\"head\")[0] || document.documentElement,\n\t\t\t\tscript = document.createElement(\"script\");\n\n\t\t\tscript.type = \"text/javascript\";\n\n\t\t\tif ( jQuery.support.scriptEval ) {\n\t\t\t\tscript.appendChild( document.createTextNode( data ) );\n\t\t\t} else {\n\t\t\t\tscript.text = data;\n\t\t\t}\n\n\t\t\t// Use insertBefore instead of appendChild to circumvent an IE6 bug.\n\t\t\t// This arises when a base node is used (#2709).\n\t\t\thead.insertBefore( script, head.firstChild );\n\t\t\thead.removeChild( script );\n\t\t}\n\t},\n\n\tnodeName: function( elem, name ) {\n\t\treturn elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();\n\t},\n\n\t// args is for internal usage only\n\teach: function( object, callback, args ) {\n\t\tvar name, i = 0,\n\t\t\tlength = object.length,\n\t\t\tisObj = length === undefined || jQuery.isFunction(object);\n\n\t\tif ( args ) {\n\t\t\tif ( isObj ) {\n\t\t\t\tfor ( name in object ) {\n\t\t\t\t\tif ( callback.apply( object[ name ], args ) === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( ; i < length; ) {\n\t\t\t\t\tif ( callback.apply( object[ i++ ], args ) === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t// A special, fast, case for the most common use of each\n\t\t} else {\n\t\t\tif ( isObj ) {\n\t\t\t\tfor ( name in object ) {\n\t\t\t\t\tif ( callback.call( object[ name ], name, object[ name ] ) === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( var value = object[0];\n\t\t\t\t\ti < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}\n\t\t\t}\n\t\t}\n\n\t\treturn object;\n\t},\n\n\ttrim: function( text ) {\n\t\treturn (text || \"\").replace( rtrim, \"\" );\n\t},\n\n\t// results is for internal usage only\n\tmakeArray: function( array, results ) {\n\t\tvar ret = results || [];\n\n\t\tif ( array != null ) {\n\t\t\t// The window, strings (and functions) also have 'length'\n\t\t\t// The extra typeof function check is to prevent crashes\n\t\t\t// in Safari 2 (See: #3039)\n\t\t\tif ( array.length == null || typeof array === \"string\" || jQuery.isFunction(array) || (typeof array !== \"function\" && array.setInterval) ) {\n\t\t\t\tpush.call( ret, array );\n\t\t\t} else {\n\t\t\t\tjQuery.merge( ret, array );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tinArray: function( elem, array ) {\n\t\tif ( array.indexOf ) {\n\t\t\treturn array.indexOf( elem );\n\t\t}\n\n\t\tfor ( var i = 0, length = array.length; i < length; i++ ) {\n\t\t\tif ( array[ i ] === elem ) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\n\t\treturn -1;\n\t},\n\n\tmerge: function( first, second ) {\n\t\tvar i = first.length, j = 0;\n\n\t\tif ( typeof second.length === \"number\" ) {\n\t\t\tfor ( var l = second.length; j < l; j++ ) {\n\t\t\t\tfirst[ i++ ] = second[ j ];\n\t\t\t}\n\t\t\n\t\t} else {\n\t\t\twhile ( second[j] !== undefined ) {\n\t\t\t\tfirst[ i++ ] = second[ j++ ];\n\t\t\t}\n\t\t}\n\n\t\tfirst.length = i;\n\n\t\treturn first;\n\t},\n\n\tgrep: function( elems, callback, inv ) {\n\t\tvar ret = [];\n\n\t\t// Go through the array, only saving the items\n\t\t// that pass the validator function\n\t\tfor ( var i = 0, length = elems.length; i < length; i++ ) {\n\t\t\tif ( !inv !== !callback( elems[ i ], i ) ) {\n\t\t\t\tret.push( elems[ i ] );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\t// arg is for internal usage only\n\tmap: function( elems, callback, arg ) {\n\t\tvar ret = [], value;\n\n\t\t// Go through the array, translating each of the items to their\n\t\t// new value (or values).\n\t\tfor ( var i = 0, length = elems.length; i < length; i++ ) {\n\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\tif ( value != null ) {\n\t\t\t\tret[ ret.length ] = value;\n\t\t\t}\n\t\t}\n\n\t\treturn ret.concat.apply( [], ret );\n\t},\n\n\t// A global GUID counter for objects\n\tguid: 1,\n\n\tproxy: function( fn, proxy, thisObject ) {\n\t\tif ( arguments.length === 2 ) {\n\t\t\tif ( typeof proxy === \"string\" ) {\n\t\t\t\tthisObject = fn;\n\t\t\t\tfn = thisObject[ proxy ];\n\t\t\t\tproxy = undefined;\n\n\t\t\t} else if ( proxy && !jQuery.isFunction( proxy ) ) {\n\t\t\t\tthisObject = proxy;\n\t\t\t\tproxy = undefined;\n\t\t\t}\n\t\t}\n\n\t\tif ( !proxy && fn ) {\n\t\t\tproxy = function() {\n\t\t\t\treturn fn.apply( thisObject || this, arguments );\n\t\t\t};\n\t\t}\n\n\t\t// Set the guid of unique handler to the same of original handler, so it can be removed\n\t\tif ( fn ) {\n\t\t\tproxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;\n\t\t}\n\n\t\t// So proxy can be declared as an argument\n\t\treturn proxy;\n\t},\n\n\t// Use of jQuery.browser is frowned upon.\n\t// More details: http://docs.jquery.com/Utilities/jQuery.browser\n\tuaMatch: function( ua ) {\n\t\tua = ua.toLowerCase();\n\n\t\tvar match = /(webkit)[ \\/]([\\w.]+)/.exec( ua ) ||\n\t\t\t/(opera)(?:.*version)?[ \\/]([\\w.]+)/.exec( ua ) ||\n\t\t\t/(msie) ([\\w.]+)/.exec( ua ) ||\n\t\t\t!/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\\w.]+))?/.exec( ua ) ||\n\t\t  \t[];\n\n\t\treturn { browser: match[1] || \"\", version: match[2] || \"0\" };\n\t},\n\n\tbrowser: {}\n});\n\nbrowserMatch = jQuery.uaMatch( userAgent );\nif ( browserMatch.browser ) {\n\tjQuery.browser[ browserMatch.browser ] = true;\n\tjQuery.browser.version = browserMatch.version;\n}\n\n// Deprecated, use jQuery.browser.webkit instead\nif ( jQuery.browser.webkit ) {\n\tjQuery.browser.safari = true;\n}\n\nif ( indexOf ) {\n\tjQuery.inArray = function( elem, array ) {\n\t\treturn indexOf.call( array, elem );\n\t};\n}\n\n// All jQuery objects should point back to these\nrootjQuery = jQuery(document);\n\n// Cleanup functions for the document ready method\nif ( document.addEventListener ) {\n\tDOMContentLoaded = function() {\n\t\tdocument.removeEventListener( \"DOMContentLoaded\", DOMContentLoaded, false );\n\t\tjQuery.ready();\n\t};\n\n} else if ( document.attachEvent ) {\n\tDOMContentLoaded = function() {\n\t\t// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).\n\t\tif ( document.readyState === \"complete\" ) {\n\t\t\tdocument.detachEvent( \"onreadystatechange\", DOMContentLoaded );\n\t\t\tjQuery.ready();\n\t\t}\n\t};\n}\n\n// The DOM ready check for Internet Explorer\nfunction doScrollCheck() {\n\tif ( jQuery.isReady ) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\t// If IE is used, use the trick by Diego Perini\n\t\t// http://javascript.nwbox.com/IEContentLoaded/\n\t\tdocument.documentElement.doScroll(\"left\");\n\t} catch( error ) {\n\t\tsetTimeout( doScrollCheck, 1 );\n\t\treturn;\n\t}\n\n\t// and execute any waiting functions\n\tjQuery.ready();\n}\n\nfunction evalScript( i, elem ) {\n\tif ( elem.src ) {\n\t\tjQuery.ajax({\n\t\t\turl: elem.src,\n\t\t\tasync: false,\n\t\t\tdataType: \"script\"\n\t\t});\n\t} else {\n\t\tjQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || \"\" );\n\t}\n\n\tif ( elem.parentNode ) {\n\t\telem.parentNode.removeChild( elem );\n\t}\n}\n\n// Mutifunctional method to get and set values to a collection\n// The value/s can be optionally by executed if its a function\nfunction access( elems, key, value, exec, fn, pass ) {\n\tvar length = elems.length;\n\t\n\t// Setting many attributes\n\tif ( typeof key === \"object\" ) {\n\t\tfor ( var k in key ) {\n\t\t\taccess( elems, k, key[k], exec, fn, value );\n\t\t}\n\t\treturn elems;\n\t}\n\t\n\t// Setting one attribute\n\tif ( value !== undefined ) {\n\t\t// Optionally, function values get executed if exec is true\n\t\texec = !pass && exec && jQuery.isFunction(value);\n\t\t\n\t\tfor ( var i = 0; i < length; i++ ) {\n\t\t\tfn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );\n\t\t}\n\t\t\n\t\treturn elems;\n\t}\n\t\n\t// Getting an attribute\n\treturn length ? fn( elems[0], key ) : undefined;\n}\n\nfunction now() {\n\treturn (new Date).getTime();\n}\n(function() {\n\n\tjQuery.support = {};\n\n\tvar root = document.documentElement,\n\t\tscript = document.createElement(\"script\"),\n\t\tdiv = document.createElement(\"div\"),\n\t\tid = \"script\" + now();\n\n\tdiv.style.display = \"none\";\n\tdiv.innerHTML = \"   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>\";\n\n\tvar all = div.getElementsByTagName(\"*\"),\n\t\ta = div.getElementsByTagName(\"a\")[0];\n\n\t// Can't get basic test support\n\tif ( !all || !all.length || !a ) {\n\t\treturn;\n\t}\n\n\tjQuery.support = {\n\t\t// IE strips leading whitespace when .innerHTML is used\n\t\tleadingWhitespace: div.firstChild.nodeType === 3,\n\n\t\t// Make sure that tbody elements aren't automatically inserted\n\t\t// IE will insert them into empty tables\n\t\ttbody: !div.getElementsByTagName(\"tbody\").length,\n\n\t\t// Make sure that link elements get serialized correctly by innerHTML\n\t\t// This requires a wrapper element in IE\n\t\thtmlSerialize: !!div.getElementsByTagName(\"link\").length,\n\n\t\t// Get the style information from getAttribute\n\t\t// (IE uses .cssText insted)\n\t\tstyle: /red/.test( a.getAttribute(\"style\") ),\n\n\t\t// Make sure that URLs aren't manipulated\n\t\t// (IE normalizes it by default)\n\t\threfNormalized: a.getAttribute(\"href\") === \"/a\",\n\n\t\t// Make sure that element opacity exists\n\t\t// (IE uses filter instead)\n\t\t// Use a regex to work around a WebKit issue. See #5145\n\t\topacity: /^0.55$/.test( a.style.opacity ),\n\n\t\t// Verify style float existence\n\t\t// (IE uses styleFloat instead of cssFloat)\n\t\tcssFloat: !!a.style.cssFloat,\n\n\t\t// Make sure that if no value is specified for a checkbox\n\t\t// that it defaults to \"on\".\n\t\t// (WebKit defaults to \"\" instead)\n\t\tcheckOn: div.getElementsByTagName(\"input\")[0].value === \"on\",\n\n\t\t// Make sure that a selected-by-default option has a working selected property.\n\t\t// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)\n\t\toptSelected: document.createElement(\"select\").appendChild( document.createElement(\"option\") ).selected,\n\n\t\tparentNode: div.removeChild( div.appendChild( document.createElement(\"div\") ) ).parentNode === null,\n\n\t\t// Will be defined later\n\t\tdeleteExpando: true,\n\t\tcheckClone: false,\n\t\tscriptEval: false,\n\t\tnoCloneEvent: true,\n\t\tboxModel: null\n\t};\n\n\tscript.type = \"text/javascript\";\n\ttry {\n\t\tscript.appendChild( document.createTextNode( \"window.\" + id + \"=1;\" ) );\n\t} catch(e) {}\n\n\troot.insertBefore( script, root.firstChild );\n\n\t// Make sure that the execution of code works by injecting a script\n\t// tag with appendChild/createTextNode\n\t// (IE doesn't support this, fails, and uses .text instead)\n\tif ( window[ id ] ) {\n\t\tjQuery.support.scriptEval = true;\n\t\tdelete window[ id ];\n\t}\n\n\t// Test to see if it's possible to delete an expando from an element\n\t// Fails in Internet Explorer\n\ttry {\n\t\tdelete script.test;\n\t\n\t} catch(e) {\n\t\tjQuery.support.deleteExpando = false;\n\t}\n\n\troot.removeChild( script );\n\n\tif ( div.attachEvent && div.fireEvent ) {\n\t\tdiv.attachEvent(\"onclick\", function click() {\n\t\t\t// Cloning a node shouldn't copy over any\n\t\t\t// bound event handlers (IE does this)\n\t\t\tjQuery.support.noCloneEvent = false;\n\t\t\tdiv.detachEvent(\"onclick\", click);\n\t\t});\n\t\tdiv.cloneNode(true).fireEvent(\"onclick\");\n\t}\n\n\tdiv = document.createElement(\"div\");\n\tdiv.innerHTML = \"<input type='radio' name='radiotest' checked='checked'/>\";\n\n\tvar fragment = document.createDocumentFragment();\n\tfragment.appendChild( div.firstChild );\n\n\t// WebKit doesn't clone checked state correctly in fragments\n\tjQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;\n\n\t// Figure out if the W3C box model works as expected\n\t// document.body must exist before we can do this\n\tjQuery(function() {\n\t\tvar div = document.createElement(\"div\");\n\t\tdiv.style.width = div.style.paddingLeft = \"1px\";\n\n\t\tdocument.body.appendChild( div );\n\t\tjQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;\n\t\tdocument.body.removeChild( div ).style.display = 'none';\n\n\t\tdiv = null;\n\t});\n\n\t// Technique from Juriy Zaytsev\n\t// http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/\n\tvar eventSupported = function( eventName ) { \n\t\tvar el = document.createElement(\"div\"); \n\t\teventName = \"on\" + eventName; \n\n\t\tvar isSupported = (eventName in el); \n\t\tif ( !isSupported ) { \n\t\t\tel.setAttribute(eventName, \"return;\"); \n\t\t\tisSupported = typeof el[eventName] === \"function\"; \n\t\t} \n\t\tel = null; \n\n\t\treturn isSupported; \n\t};\n\t\n\tjQuery.support.submitBubbles = eventSupported(\"submit\");\n\tjQuery.support.changeBubbles = eventSupported(\"change\");\n\n\t// release memory in IE\n\troot = script = div = all = a = null;\n})();\n\njQuery.props = {\n\t\"for\": \"htmlFor\",\n\t\"class\": \"className\",\n\treadonly: \"readOnly\",\n\tmaxlength: \"maxLength\",\n\tcellspacing: \"cellSpacing\",\n\trowspan: \"rowSpan\",\n\tcolspan: \"colSpan\",\n\ttabindex: \"tabIndex\",\n\tusemap: \"useMap\",\n\tframeborder: \"frameBorder\"\n};\nvar expando = \"jQuery\" + now(), uuid = 0, windowData = {};\n\njQuery.extend({\n\tcache: {},\n\t\n\texpando:expando,\n\n\t// The following elements throw uncatchable exceptions if you\n\t// attempt to add expando properties to them.\n\tnoData: {\n\t\t\"embed\": true,\n\t\t\"object\": true,\n\t\t\"applet\": true\n\t},\n\n\tdata: function( elem, name, data ) {\n\t\tif ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {\n\t\t\treturn;\n\t\t}\n\n\t\telem = elem == window ?\n\t\t\twindowData :\n\t\t\telem;\n\n\t\tvar id = elem[ expando ], cache = jQuery.cache, thisCache;\n\n\t\tif ( !id && typeof name === \"string\" && data === undefined ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Compute a unique ID for the element\n\t\tif ( !id ) { \n\t\t\tid = ++uuid;\n\t\t}\n\n\t\t// Avoid generating a new cache unless none exists and we\n\t\t// want to manipulate it.\n\t\tif ( typeof name === \"object\" ) {\n\t\t\telem[ expando ] = id;\n\t\t\tthisCache = cache[ id ] = jQuery.extend(true, {}, name);\n\n\t\t} else if ( !cache[ id ] ) {\n\t\t\telem[ expando ] = id;\n\t\t\tcache[ id ] = {};\n\t\t}\n\n\t\tthisCache = cache[ id ];\n\n\t\t// Prevent overriding the named cache with undefined values\n\t\tif ( data !== undefined ) {\n\t\t\tthisCache[ name ] = data;\n\t\t}\n\n\t\treturn typeof name === \"string\" ? thisCache[ name ] : thisCache;\n\t},\n\n\tremoveData: function( elem, name ) {\n\t\tif ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {\n\t\t\treturn;\n\t\t}\n\n\t\telem = elem == window ?\n\t\t\twindowData :\n\t\t\telem;\n\n\t\tvar id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];\n\n\t\t// If we want to remove a specific section of the element's data\n\t\tif ( name ) {\n\t\t\tif ( thisCache ) {\n\t\t\t\t// Remove the section of cache data\n\t\t\t\tdelete thisCache[ name ];\n\n\t\t\t\t// If we've removed all the data, remove the element's cache\n\t\t\t\tif ( jQuery.isEmptyObject(thisCache) ) {\n\t\t\t\t\tjQuery.removeData( elem );\n\t\t\t\t}\n\t\t\t}\n\n\t\t// Otherwise, we want to remove all of the element's data\n\t\t} else {\n\t\t\tif ( jQuery.support.deleteExpando ) {\n\t\t\t\tdelete elem[ jQuery.expando ];\n\n\t\t\t} else if ( elem.removeAttribute ) {\n\t\t\t\telem.removeAttribute( jQuery.expando );\n\t\t\t}\n\n\t\t\t// Completely remove the data cache\n\t\t\tdelete cache[ id ];\n\t\t}\n\t}\n});\n\njQuery.fn.extend({\n\tdata: function( key, value ) {\n\t\tif ( typeof key === \"undefined\" && this.length ) {\n\t\t\treturn jQuery.data( this[0] );\n\n\t\t} else if ( typeof key === \"object\" ) {\n\t\t\treturn this.each(function() {\n\t\t\t\tjQuery.data( this, key );\n\t\t\t});\n\t\t}\n\n\t\tvar parts = key.split(\".\");\n\t\tparts[1] = parts[1] ? \".\" + parts[1] : \"\";\n\n\t\tif ( value === undefined ) {\n\t\t\tvar data = this.triggerHandler(\"getData\" + parts[1] + \"!\", [parts[0]]);\n\n\t\t\tif ( data === undefined && this.length ) {\n\t\t\t\tdata = jQuery.data( this[0], key );\n\t\t\t}\n\t\t\treturn data === undefined && parts[1] ?\n\t\t\t\tthis.data( parts[0] ) :\n\t\t\t\tdata;\n\t\t} else {\n\t\t\treturn this.trigger(\"setData\" + parts[1] + \"!\", [parts[0], value]).each(function() {\n\t\t\t\tjQuery.data( this, key, value );\n\t\t\t});\n\t\t}\n\t},\n\n\tremoveData: function( key ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.removeData( this, key );\n\t\t});\n\t}\n});\njQuery.extend({\n\tqueue: function( elem, type, data ) {\n\t\tif ( !elem ) {\n\t\t\treturn;\n\t\t}\n\n\t\ttype = (type || \"fx\") + \"queue\";\n\t\tvar q = jQuery.data( elem, type );\n\n\t\t// Speed up dequeue by getting out quickly if this is just a lookup\n\t\tif ( !data ) {\n\t\t\treturn q || [];\n\t\t}\n\n\t\tif ( !q || jQuery.isArray(data) ) {\n\t\t\tq = jQuery.data( elem, type, jQuery.makeArray(data) );\n\n\t\t} else {\n\t\t\tq.push( data );\n\t\t}\n\n\t\treturn q;\n\t},\n\n\tdequeue: function( elem, type ) {\n\t\ttype = type || \"fx\";\n\n\t\tvar queue = jQuery.queue( elem, type ), fn = queue.shift();\n\n\t\t// If the fx queue is dequeued, always remove the progress sentinel\n\t\tif ( fn === \"inprogress\" ) {\n\t\t\tfn = queue.shift();\n\t\t}\n\n\t\tif ( fn ) {\n\t\t\t// Add a progress sentinel to prevent the fx queue from being\n\t\t\t// automatically dequeued\n\t\t\tif ( type === \"fx\" ) {\n\t\t\t\tqueue.unshift(\"inprogress\");\n\t\t\t}\n\n\t\t\tfn.call(elem, function() {\n\t\t\t\tjQuery.dequeue(elem, type);\n\t\t\t});\n\t\t}\n\t}\n});\n\njQuery.fn.extend({\n\tqueue: function( type, data ) {\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tdata = type;\n\t\t\ttype = \"fx\";\n\t\t}\n\n\t\tif ( data === undefined ) {\n\t\t\treturn jQuery.queue( this[0], type );\n\t\t}\n\t\treturn this.each(function( i, elem ) {\n\t\t\tvar queue = jQuery.queue( this, type, data );\n\n\t\t\tif ( type === \"fx\" && queue[0] !== \"inprogress\" ) {\n\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t}\n\t\t});\n\t},\n\tdequeue: function( type ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.dequeue( this, type );\n\t\t});\n\t},\n\n\t// Based off of the plugin by Clint Helfers, with permission.\n\t// http://blindsignals.com/index.php/2009/07/jquery-delay/\n\tdelay: function( time, type ) {\n\t\ttime = jQuery.fx ? jQuery.fx.speeds[time] || time : time;\n\t\ttype = type || \"fx\";\n\n\t\treturn this.queue( type, function() {\n\t\t\tvar elem = this;\n\t\t\tsetTimeout(function() {\n\t\t\t\tjQuery.dequeue( elem, type );\n\t\t\t}, time );\n\t\t});\n\t},\n\n\tclearQueue: function( type ) {\n\t\treturn this.queue( type || \"fx\", [] );\n\t}\n});\nvar rclass = /[\\n\\t]/g,\n\trspace = /\\s+/,\n\trreturn = /\\r/g,\n\trspecialurl = /href|src|style/,\n\trtype = /(button|input)/i,\n\trfocusable = /(button|input|object|select|textarea)/i,\n\trclickable = /^(a|area)$/i,\n\trradiocheck = /radio|checkbox/;\n\njQuery.fn.extend({\n\tattr: function( name, value ) {\n\t\treturn access( this, name, value, true, jQuery.attr );\n\t},\n\n\tremoveAttr: function( name, fn ) {\n\t\treturn this.each(function(){\n\t\t\tjQuery.attr( this, name, \"\" );\n\t\t\tif ( this.nodeType === 1 ) {\n\t\t\t\tthis.removeAttribute( name );\n\t\t\t}\n\t\t});\n\t},\n\n\taddClass: function( value ) {\n\t\tif ( jQuery.isFunction(value) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery(this);\n\t\t\t\tself.addClass( value.call(this, i, self.attr(\"class\")) );\n\t\t\t});\n\t\t}\n\n\t\tif ( value && typeof value === \"string\" ) {\n\t\t\tvar classNames = (value || \"\").split( rspace );\n\n\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\tvar elem = this[i];\n\n\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\tif ( !elem.className ) {\n\t\t\t\t\t\telem.className = value;\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar className = \" \" + elem.className + \" \", setClass = elem.className;\n\t\t\t\t\t\tfor ( var c = 0, cl = classNames.length; c < cl; c++ ) {\n\t\t\t\t\t\t\tif ( className.indexOf( \" \" + classNames[c] + \" \" ) < 0 ) {\n\t\t\t\t\t\t\t\tsetClass += \" \" + classNames[c];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telem.className = jQuery.trim( setClass );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tremoveClass: function( value ) {\n\t\tif ( jQuery.isFunction(value) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery(this);\n\t\t\t\tself.removeClass( value.call(this, i, self.attr(\"class\")) );\n\t\t\t});\n\t\t}\n\n\t\tif ( (value && typeof value === \"string\") || value === undefined ) {\n\t\t\tvar classNames = (value || \"\").split(rspace);\n\n\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\tvar elem = this[i];\n\n\t\t\t\tif ( elem.nodeType === 1 && elem.className ) {\n\t\t\t\t\tif ( value ) {\n\t\t\t\t\t\tvar className = (\" \" + elem.className + \" \").replace(rclass, \" \");\n\t\t\t\t\t\tfor ( var c = 0, cl = classNames.length; c < cl; c++ ) {\n\t\t\t\t\t\t\tclassName = className.replace(\" \" + classNames[c] + \" \", \" \");\n\t\t\t\t\t\t}\n\t\t\t\t\t\telem.className = jQuery.trim( className );\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\telem.className = \"\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\ttoggleClass: function( value, stateVal ) {\n\t\tvar type = typeof value, isBool = typeof stateVal === \"boolean\";\n\n\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery(this);\n\t\t\t\tself.toggleClass( value.call(this, i, self.attr(\"class\"), stateVal), stateVal );\n\t\t\t});\n\t\t}\n\n\t\treturn this.each(function() {\n\t\t\tif ( type === \"string\" ) {\n\t\t\t\t// toggle individual class names\n\t\t\t\tvar className, i = 0, self = jQuery(this),\n\t\t\t\t\tstate = stateVal,\n\t\t\t\t\tclassNames = value.split( rspace );\n\n\t\t\t\twhile ( (className = classNames[ i++ ]) ) {\n\t\t\t\t\t// check each className given, space seperated list\n\t\t\t\t\tstate = isBool ? state : !self.hasClass( className );\n\t\t\t\t\tself[ state ? \"addClass\" : \"removeClass\" ]( className );\n\t\t\t\t}\n\n\t\t\t} else if ( type === \"undefined\" || type === \"boolean\" ) {\n\t\t\t\tif ( this.className ) {\n\t\t\t\t\t// store className if set\n\t\t\t\t\tjQuery.data( this, \"__className__\", this.className );\n\t\t\t\t}\n\n\t\t\t\t// toggle whole className\n\t\t\t\tthis.className = this.className || value === false ? \"\" : jQuery.data( this, \"__className__\" ) || \"\";\n\t\t\t}\n\t\t});\n\t},\n\n\thasClass: function( selector ) {\n\t\tvar className = \" \" + selector + \" \";\n\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\tif ( (\" \" + this[i].className + \" \").replace(rclass, \" \").indexOf( className ) > -1 ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t},\n\n\tval: function( value ) {\n\t\tif ( value === undefined ) {\n\t\t\tvar elem = this[0];\n\n\t\t\tif ( elem ) {\n\t\t\t\tif ( jQuery.nodeName( elem, \"option\" ) ) {\n\t\t\t\t\treturn (elem.attributes.value || {}).specified ? elem.value : elem.text;\n\t\t\t\t}\n\n\t\t\t\t// We need to handle select boxes special\n\t\t\t\tif ( jQuery.nodeName( elem, \"select\" ) ) {\n\t\t\t\t\tvar index = elem.selectedIndex,\n\t\t\t\t\t\tvalues = [],\n\t\t\t\t\t\toptions = elem.options,\n\t\t\t\t\t\tone = elem.type === \"select-one\";\n\n\t\t\t\t\t// Nothing was selected\n\t\t\t\t\tif ( index < 0 ) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Loop through all the selected options\n\t\t\t\t\tfor ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {\n\t\t\t\t\t\tvar option = options[ i ];\n\n\t\t\t\t\t\tif ( option.selected ) {\n\t\t\t\t\t\t\t// Get the specifc value for the option\n\t\t\t\t\t\t\tvalue = jQuery(option).val();\n\n\t\t\t\t\t\t\t// We don't need an array for one selects\n\t\t\t\t\t\t\tif ( one ) {\n\t\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Multi-Selects return an array\n\t\t\t\t\t\t\tvalues.push( value );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn values;\n\t\t\t\t}\n\n\t\t\t\t// Handle the case where in Webkit \"\" is returned instead of \"on\" if a value isn't specified\n\t\t\t\tif ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {\n\t\t\t\t\treturn elem.getAttribute(\"value\") === null ? \"on\" : elem.value;\n\t\t\t\t}\n\t\t\t\t\n\n\t\t\t\t// Everything else, we just grab the value\n\t\t\t\treturn (elem.value || \"\").replace(rreturn, \"\");\n\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tvar isFunction = jQuery.isFunction(value);\n\n\t\treturn this.each(function(i) {\n\t\t\tvar self = jQuery(this), val = value;\n\n\t\t\tif ( this.nodeType !== 1 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( isFunction ) {\n\t\t\t\tval = value.call(this, i, self.val());\n\t\t\t}\n\n\t\t\t// Typecast each time if the value is a Function and the appended\n\t\t\t// value is therefore different each time.\n\t\t\tif ( typeof val === \"number\" ) {\n\t\t\t\tval += \"\";\n\t\t\t}\n\n\t\t\tif ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {\n\t\t\t\tthis.checked = jQuery.inArray( self.val(), val ) >= 0;\n\n\t\t\t} else if ( jQuery.nodeName( this, \"select\" ) ) {\n\t\t\t\tvar values = jQuery.makeArray(val);\n\n\t\t\t\tjQuery( \"option\", this ).each(function() {\n\t\t\t\t\tthis.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;\n\t\t\t\t});\n\n\t\t\t\tif ( !values.length ) {\n\t\t\t\t\tthis.selectedIndex = -1;\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tthis.value = val;\n\t\t\t}\n\t\t});\n\t}\n});\n\njQuery.extend({\n\tattrFn: {\n\t\tval: true,\n\t\tcss: true,\n\t\thtml: true,\n\t\ttext: true,\n\t\tdata: true,\n\t\twidth: true,\n\t\theight: true,\n\t\toffset: true\n\t},\n\t\t\n\tattr: function( elem, name, value, pass ) {\n\t\t// don't set attributes on text and comment nodes\n\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif ( pass && name in jQuery.attrFn ) {\n\t\t\treturn jQuery(elem)[name](value);\n\t\t}\n\n\t\tvar notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),\n\t\t\t// Whether we are setting (or getting)\n\t\t\tset = value !== undefined;\n\n\t\t// Try to normalize/fix the name\n\t\tname = notxml && jQuery.props[ name ] || name;\n\n\t\t// Only do all the following if this is a node (faster for style)\n\t\tif ( elem.nodeType === 1 ) {\n\t\t\t// These attributes require special treatment\n\t\t\tvar special = rspecialurl.test( name );\n\n\t\t\t// Safari mis-reports the default selected property of an option\n\t\t\t// Accessing the parent's selectedIndex property fixes it\n\t\t\tif ( name === \"selected\" && !jQuery.support.optSelected ) {\n\t\t\t\tvar parent = elem.parentNode;\n\t\t\t\tif ( parent ) {\n\t\t\t\t\tparent.selectedIndex;\n\t\n\t\t\t\t\t// Make sure that it also works with optgroups, see #5701\n\t\t\t\t\tif ( parent.parentNode ) {\n\t\t\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If applicable, access the attribute via the DOM 0 way\n\t\t\tif ( name in elem && notxml && !special ) {\n\t\t\t\tif ( set ) {\n\t\t\t\t\t// We can't allow the type property to be changed (since it causes problems in IE)\n\t\t\t\t\tif ( name === \"type\" && rtype.test( elem.nodeName ) && elem.parentNode ) {\n\t\t\t\t\t\tjQuery.error( \"type property can't be changed\" );\n\t\t\t\t\t}\n\n\t\t\t\t\telem[ name ] = value;\n\t\t\t\t}\n\n\t\t\t\t// browsers index elements by id/name on forms, give priority to attributes.\n\t\t\t\tif ( jQuery.nodeName( elem, \"form\" ) && elem.getAttributeNode(name) ) {\n\t\t\t\t\treturn elem.getAttributeNode( name ).nodeValue;\n\t\t\t\t}\n\n\t\t\t\t// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set\n\t\t\t\t// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/\n\t\t\t\tif ( name === \"tabIndex\" ) {\n\t\t\t\t\tvar attributeNode = elem.getAttributeNode( \"tabIndex\" );\n\n\t\t\t\t\treturn attributeNode && attributeNode.specified ?\n\t\t\t\t\t\tattributeNode.value :\n\t\t\t\t\t\trfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?\n\t\t\t\t\t\t\t0 :\n\t\t\t\t\t\t\tundefined;\n\t\t\t\t}\n\n\t\t\t\treturn elem[ name ];\n\t\t\t}\n\n\t\t\tif ( !jQuery.support.style && notxml && name === \"style\" ) {\n\t\t\t\tif ( set ) {\n\t\t\t\t\telem.style.cssText = \"\" + value;\n\t\t\t\t}\n\n\t\t\t\treturn elem.style.cssText;\n\t\t\t}\n\n\t\t\tif ( set ) {\n\t\t\t\t// convert the value to a string (all browsers do this but IE) see #1070\n\t\t\t\telem.setAttribute( name, \"\" + value );\n\t\t\t}\n\n\t\t\tvar attr = !jQuery.support.hrefNormalized && notxml && special ?\n\t\t\t\t\t// Some attributes require a special call on IE\n\t\t\t\t\telem.getAttribute( name, 2 ) :\n\t\t\t\t\telem.getAttribute( name );\n\n\t\t\t// Non-existent attributes return null, we normalize to undefined\n\t\t\treturn attr === null ? undefined : attr;\n\t\t}\n\n\t\t// elem is actually elem.style ... set the style\n\t\t// Using attr for specific style information is now deprecated. Use style instead.\n\t\treturn jQuery.style( elem, name, value );\n\t}\n});\nvar rnamespaces = /\\.(.*)$/,\n\tfcleanup = function( nm ) {\n\t\treturn nm.replace(/[^\\w\\s\\.\\|`]/g, function( ch ) {\n\t\t\treturn \"\\\\\" + ch;\n\t\t});\n\t};\n\n/*\n * A number of helper functions used for managing events.\n * Many of the ideas behind this code originated from\n * Dean Edwards' addEvent library.\n */\njQuery.event = {\n\n\t// Bind an event to an element\n\t// Original by Dean Edwards\n\tadd: function( elem, types, handler, data ) {\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// For whatever reason, IE has trouble passing the window object\n\t\t// around, causing it to be cloned in the process\n\t\tif ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {\n\t\t\telem = window;\n\t\t}\n\n\t\tvar handleObjIn, handleObj;\n\n\t\tif ( handler.handler ) {\n\t\t\thandleObjIn = handler;\n\t\t\thandler = handleObjIn.handler;\n\t\t}\n\n\t\t// Make sure that the function being executed has a unique ID\n\t\tif ( !handler.guid ) {\n\t\t\thandler.guid = jQuery.guid++;\n\t\t}\n\n\t\t// Init the element's event structure\n\t\tvar elemData = jQuery.data( elem );\n\n\t\t// If no elemData is found then we must be trying to bind to one of the\n\t\t// banned noData elements\n\t\tif ( !elemData ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar events = elemData.events = elemData.events || {},\n\t\t\teventHandle = elemData.handle, eventHandle;\n\n\t\tif ( !eventHandle ) {\n\t\t\telemData.handle = eventHandle = function() {\n\t\t\t\t// Handle the second event of a trigger and when\n\t\t\t\t// an event is called after a page has unloaded\n\t\t\t\treturn typeof jQuery !== \"undefined\" && !jQuery.event.triggered ?\n\t\t\t\t\tjQuery.event.handle.apply( eventHandle.elem, arguments ) :\n\t\t\t\t\tundefined;\n\t\t\t};\n\t\t}\n\n\t\t// Add elem as a property of the handle function\n\t\t// This is to prevent a memory leak with non-native events in IE.\n\t\teventHandle.elem = elem;\n\n\t\t// Handle multiple events separated by a space\n\t\t// jQuery(...).bind(\"mouseover mouseout\", fn);\n\t\ttypes = types.split(\" \");\n\n\t\tvar type, i = 0, namespaces;\n\n\t\twhile ( (type = types[ i++ ]) ) {\n\t\t\thandleObj = handleObjIn ?\n\t\t\t\tjQuery.extend({}, handleObjIn) :\n\t\t\t\t{ handler: handler, data: data };\n\n\t\t\t// Namespaced event handlers\n\t\t\tif ( type.indexOf(\".\") > -1 ) {\n\t\t\t\tnamespaces = type.split(\".\");\n\t\t\t\ttype = namespaces.shift();\n\t\t\t\thandleObj.namespace = namespaces.slice(0).sort().join(\".\");\n\n\t\t\t} else {\n\t\t\t\tnamespaces = [];\n\t\t\t\thandleObj.namespace = \"\";\n\t\t\t}\n\n\t\t\thandleObj.type = type;\n\t\t\thandleObj.guid = handler.guid;\n\n\t\t\t// Get the current list of functions bound to this event\n\t\t\tvar handlers = events[ type ],\n\t\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// Init the event handler queue\n\t\t\tif ( !handlers ) {\n\t\t\t\thandlers = events[ type ] = [];\n\n\t\t\t\t// Check for a special event handler\n\t\t\t\t// Only use addEventListener/attachEvent if the special\n\t\t\t\t// events handler returns false\n\t\t\t\tif ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n\t\t\t\t\t// Bind the global event handler to the element\n\t\t\t\t\tif ( elem.addEventListener ) {\n\t\t\t\t\t\telem.addEventListener( type, eventHandle, false );\n\n\t\t\t\t\t} else if ( elem.attachEvent ) {\n\t\t\t\t\t\telem.attachEvent( \"on\" + type, eventHandle );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\tif ( special.add ) { \n\t\t\t\tspecial.add.call( elem, handleObj ); \n\n\t\t\t\tif ( !handleObj.handler.guid ) {\n\t\t\t\t\thandleObj.handler.guid = handler.guid;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add the function to the element's handler list\n\t\t\thandlers.push( handleObj );\n\n\t\t\t// Keep track of which events have been used, for global triggering\n\t\t\tjQuery.event.global[ type ] = true;\n\t\t}\n\n\t\t// Nullify elem to prevent memory leaks in IE\n\t\telem = null;\n\t},\n\n\tglobal: {},\n\n\t// Detach an event or set of events from an element\n\tremove: function( elem, types, handler, pos ) {\n\t\t// don't do events on text and comment nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,\n\t\t\telemData = jQuery.data( elem ),\n\t\t\tevents = elemData && elemData.events;\n\n\t\tif ( !elemData || !events ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// types is actually an event object here\n\t\tif ( types && types.type ) {\n\t\t\thandler = types.handler;\n\t\t\ttypes = types.type;\n\t\t}\n\n\t\t// Unbind all events for the element\n\t\tif ( !types || typeof types === \"string\" && types.charAt(0) === \".\" ) {\n\t\t\ttypes = types || \"\";\n\n\t\t\tfor ( type in events ) {\n\t\t\t\tjQuery.event.remove( elem, type + types );\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle multiple events separated by a space\n\t\t// jQuery(...).unbind(\"mouseover mouseout\", fn);\n\t\ttypes = types.split(\" \");\n\n\t\twhile ( (type = types[ i++ ]) ) {\n\t\t\torigType = type;\n\t\t\thandleObj = null;\n\t\t\tall = type.indexOf(\".\") < 0;\n\t\t\tnamespaces = [];\n\n\t\t\tif ( !all ) {\n\t\t\t\t// Namespaced event handlers\n\t\t\t\tnamespaces = type.split(\".\");\n\t\t\t\ttype = namespaces.shift();\n\n\t\t\t\tnamespace = new RegExp(\"(^|\\\\.)\" + \n\t\t\t\t\tjQuery.map( namespaces.slice(0).sort(), fcleanup ).join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\")\n\t\t\t}\n\n\t\t\teventType = events[ type ];\n\n\t\t\tif ( !eventType ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( !handler ) {\n\t\t\t\tfor ( var j = 0; j < eventType.length; j++ ) {\n\t\t\t\t\thandleObj = eventType[ j ];\n\n\t\t\t\t\tif ( all || namespace.test( handleObj.namespace ) ) {\n\t\t\t\t\t\tjQuery.event.remove( elem, origType, handleObj.handler, j );\n\t\t\t\t\t\teventType.splice( j--, 1 );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\tfor ( var j = pos || 0; j < eventType.length; j++ ) {\n\t\t\t\thandleObj = eventType[ j ];\n\n\t\t\t\tif ( handler.guid === handleObj.guid ) {\n\t\t\t\t\t// remove the given handler for the given type\n\t\t\t\t\tif ( all || namespace.test( handleObj.namespace ) ) {\n\t\t\t\t\t\tif ( pos == null ) {\n\t\t\t\t\t\t\teventType.splice( j--, 1 );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( special.remove ) {\n\t\t\t\t\t\t\tspecial.remove.call( elem, handleObj );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( pos != null ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// remove generic event handler if no more handlers exist\n\t\t\tif ( eventType.length === 0 || pos != null && eventType.length === 1 ) {\n\t\t\t\tif ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {\n\t\t\t\t\tremoveEvent( elem, type, elemData.handle );\n\t\t\t\t}\n\n\t\t\t\tret = null;\n\t\t\t\tdelete events[ type ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove the expando if it's no longer used\n\t\tif ( jQuery.isEmptyObject( events ) ) {\n\t\t\tvar handle = elemData.handle;\n\t\t\tif ( handle ) {\n\t\t\t\thandle.elem = null;\n\t\t\t}\n\n\t\t\tdelete elemData.events;\n\t\t\tdelete elemData.handle;\n\n\t\t\tif ( jQuery.isEmptyObject( elemData ) ) {\n\t\t\t\tjQuery.removeData( elem );\n\t\t\t}\n\t\t}\n\t},\n\n\t// bubbling is internal\n\ttrigger: function( event, data, elem /*, bubbling */ ) {\n\t\t// Event object or event type\n\t\tvar type = event.type || event,\n\t\t\tbubbling = arguments[3];\n\n\t\tif ( !bubbling ) {\n\t\t\tevent = typeof event === \"object\" ?\n\t\t\t\t// jQuery.Event object\n\t\t\t\tevent[expando] ? event :\n\t\t\t\t// Object literal\n\t\t\t\tjQuery.extend( jQuery.Event(type), event ) :\n\t\t\t\t// Just the event type (string)\n\t\t\t\tjQuery.Event(type);\n\n\t\t\tif ( type.indexOf(\"!\") >= 0 ) {\n\t\t\t\tevent.type = type = type.slice(0, -1);\n\t\t\t\tevent.exclusive = true;\n\t\t\t}\n\n\t\t\t// Handle a global trigger\n\t\t\tif ( !elem ) {\n\t\t\t\t// Don't bubble custom events when global (to avoid too much overhead)\n\t\t\t\tevent.stopPropagation();\n\n\t\t\t\t// Only trigger if we've ever bound an event for it\n\t\t\t\tif ( jQuery.event.global[ type ] ) {\n\t\t\t\t\tjQuery.each( jQuery.cache, function() {\n\t\t\t\t\t\tif ( this.events && this.events[type] ) {\n\t\t\t\t\t\t\tjQuery.event.trigger( event, data, this.handle.elem );\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle triggering a single element\n\n\t\t\t// don't do events on text and comment nodes\n\t\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Clean up in case it is reused\n\t\t\tevent.result = undefined;\n\t\t\tevent.target = elem;\n\n\t\t\t// Clone the incoming data, if any\n\t\t\tdata = jQuery.makeArray( data );\n\t\t\tdata.unshift( event );\n\t\t}\n\n\t\tevent.currentTarget = elem;\n\n\t\t// Trigger the event, it is assumed that \"handle\" is a function\n\t\tvar handle = jQuery.data( elem, \"handle\" );\n\t\tif ( handle ) {\n\t\t\thandle.apply( elem, data );\n\t\t}\n\n\t\tvar parent = elem.parentNode || elem.ownerDocument;\n\n\t\t// Trigger an inline bound script\n\t\ttry {\n\t\t\tif ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {\n\t\t\t\tif ( elem[ \"on\" + type ] && elem[ \"on\" + type ].apply( elem, data ) === false ) {\n\t\t\t\t\tevent.result = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t// prevent IE from throwing an error for some elements with some event types, see #3533\n\t\t} catch (e) {}\n\n\t\tif ( !event.isPropagationStopped() && parent ) {\n\t\t\tjQuery.event.trigger( event, data, parent, true );\n\n\t\t} else if ( !event.isDefaultPrevented() ) {\n\t\t\tvar target = event.target, old,\n\t\t\t\tisClick = jQuery.nodeName(target, \"a\") && type === \"click\",\n\t\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\tif ( (!special._default || special._default.call( elem, event ) === false) && \n\t\t\t\t!isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {\n\n\t\t\t\ttry {\n\t\t\t\t\tif ( target[ type ] ) {\n\t\t\t\t\t\t// Make sure that we don't accidentally re-trigger the onFOO events\n\t\t\t\t\t\told = target[ \"on\" + type ];\n\n\t\t\t\t\t\tif ( old ) {\n\t\t\t\t\t\t\ttarget[ \"on\" + type ] = null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tjQuery.event.triggered = true;\n\t\t\t\t\t\ttarget[ type ]();\n\t\t\t\t\t}\n\n\t\t\t\t// prevent IE from throwing an error for some elements with some event types, see #3533\n\t\t\t\t} catch (e) {}\n\n\t\t\t\tif ( old ) {\n\t\t\t\t\ttarget[ \"on\" + type ] = old;\n\t\t\t\t}\n\n\t\t\t\tjQuery.event.triggered = false;\n\t\t\t}\n\t\t}\n\t},\n\n\thandle: function( event ) {\n\t\tvar all, handlers, namespaces, namespace, events;\n\n\t\tevent = arguments[0] = jQuery.event.fix( event || window.event );\n\t\tevent.currentTarget = this;\n\n\t\t// Namespaced event handlers\n\t\tall = event.type.indexOf(\".\") < 0 && !event.exclusive;\n\n\t\tif ( !all ) {\n\t\t\tnamespaces = event.type.split(\".\");\n\t\t\tevent.type = namespaces.shift();\n\t\t\tnamespace = new RegExp(\"(^|\\\\.)\" + namespaces.slice(0).sort().join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\");\n\t\t}\n\n\t\tvar events = jQuery.data(this, \"events\"), handlers = events[ event.type ];\n\n\t\tif ( events && handlers ) {\n\t\t\t// Clone the handlers to prevent manipulation\n\t\t\thandlers = handlers.slice(0);\n\n\t\t\tfor ( var j = 0, l = handlers.length; j < l; j++ ) {\n\t\t\t\tvar handleObj = handlers[ j ];\n\n\t\t\t\t// Filter the functions by class\n\t\t\t\tif ( all || namespace.test( handleObj.namespace ) ) {\n\t\t\t\t\t// Pass in a reference to the handler function itself\n\t\t\t\t\t// So that we can later remove it\n\t\t\t\t\tevent.handler = handleObj.handler;\n\t\t\t\t\tevent.data = handleObj.data;\n\t\t\t\t\tevent.handleObj = handleObj;\n\t\n\t\t\t\t\tvar ret = handleObj.handler.apply( this, arguments );\n\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\tevent.result = ret;\n\t\t\t\t\t\tif ( ret === false ) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( event.isImmediatePropagationStopped() ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\tprops: \"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which\".split(\" \"),\n\n\tfix: function( event ) {\n\t\tif ( event[ expando ] ) {\n\t\t\treturn event;\n\t\t}\n\n\t\t// store a copy of the original event object\n\t\t// and \"clone\" to set read-only properties\n\t\tvar originalEvent = event;\n\t\tevent = jQuery.Event( originalEvent );\n\n\t\tfor ( var i = this.props.length, prop; i; ) {\n\t\t\tprop = this.props[ --i ];\n\t\t\tevent[ prop ] = originalEvent[ prop ];\n\t\t}\n\n\t\t// Fix target property, if necessary\n\t\tif ( !event.target ) {\n\t\t\tevent.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either\n\t\t}\n\n\t\t// check if target is a textnode (safari)\n\t\tif ( event.target.nodeType === 3 ) {\n\t\t\tevent.target = event.target.parentNode;\n\t\t}\n\n\t\t// Add relatedTarget, if necessary\n\t\tif ( !event.relatedTarget && event.fromElement ) {\n\t\t\tevent.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;\n\t\t}\n\n\t\t// Calculate pageX/Y if missing and clientX/Y available\n\t\tif ( event.pageX == null && event.clientX != null ) {\n\t\t\tvar doc = document.documentElement, body = document.body;\n\t\t\tevent.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);\n\t\t\tevent.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);\n\t\t}\n\n\t\t// Add which for key events\n\t\tif ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {\n\t\t\tevent.which = event.charCode || event.keyCode;\n\t\t}\n\n\t\t// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)\n\t\tif ( !event.metaKey && event.ctrlKey ) {\n\t\t\tevent.metaKey = event.ctrlKey;\n\t\t}\n\n\t\t// Add which for click: 1 === left; 2 === middle; 3 === right\n\t\t// Note: button is not normalized, so don't use it\n\t\tif ( !event.which && event.button !== undefined ) {\n\t\t\tevent.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));\n\t\t}\n\n\t\treturn event;\n\t},\n\n\t// Deprecated, use jQuery.guid instead\n\tguid: 1E8,\n\n\t// Deprecated, use jQuery.proxy instead\n\tproxy: jQuery.proxy,\n\n\tspecial: {\n\t\tready: {\n\t\t\t// Make sure the ready event is setup\n\t\t\tsetup: jQuery.bindReady,\n\t\t\tteardown: jQuery.noop\n\t\t},\n\n\t\tlive: {\n\t\t\tadd: function( handleObj ) {\n\t\t\t\tjQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) ); \n\t\t\t},\n\n\t\t\tremove: function( handleObj ) {\n\t\t\t\tvar remove = true,\n\t\t\t\t\ttype = handleObj.origType.replace(rnamespaces, \"\");\n\t\t\t\t\n\t\t\t\tjQuery.each( jQuery.data(this, \"events\").live || [], function() {\n\t\t\t\t\tif ( type === this.origType.replace(rnamespaces, \"\") ) {\n\t\t\t\t\t\tremove = false;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif ( remove ) {\n\t\t\t\t\tjQuery.event.remove( this, handleObj.origType, liveHandler );\n\t\t\t\t}\n\t\t\t}\n\n\t\t},\n\n\t\tbeforeunload: {\n\t\t\tsetup: function( data, namespaces, eventHandle ) {\n\t\t\t\t// We only want to do this special case on windows\n\t\t\t\tif ( this.setInterval ) {\n\t\t\t\t\tthis.onbeforeunload = eventHandle;\n\t\t\t\t}\n\n\t\t\t\treturn false;\n\t\t\t},\n\t\t\tteardown: function( namespaces, eventHandle ) {\n\t\t\t\tif ( this.onbeforeunload === eventHandle ) {\n\t\t\t\t\tthis.onbeforeunload = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\nvar removeEvent = document.removeEventListener ?\n\tfunction( elem, type, handle ) {\n\t\telem.removeEventListener( type, handle, false );\n\t} : \n\tfunction( elem, type, handle ) {\n\t\telem.detachEvent( \"on\" + type, handle );\n\t};\n\njQuery.Event = function( src ) {\n\t// Allow instantiation without the 'new' keyword\n\tif ( !this.preventDefault ) {\n\t\treturn new jQuery.Event( src );\n\t}\n\n\t// Event object\n\tif ( src && src.type ) {\n\t\tthis.originalEvent = src;\n\t\tthis.type = src.type;\n\t// Event type\n\t} else {\n\t\tthis.type = src;\n\t}\n\n\t// timeStamp is buggy for some events on Firefox(#3843)\n\t// So we won't rely on the native value\n\tthis.timeStamp = now();\n\n\t// Mark it as fixed\n\tthis[ expando ] = true;\n};\n\nfunction returnFalse() {\n\treturn false;\n}\nfunction returnTrue() {\n\treturn true;\n}\n\n// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding\n// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\njQuery.Event.prototype = {\n\tpreventDefault: function() {\n\t\tthis.isDefaultPrevented = returnTrue;\n\n\t\tvar e = this.originalEvent;\n\t\tif ( !e ) {\n\t\t\treturn;\n\t\t}\n\t\t\n\t\t// if preventDefault exists run it on the original event\n\t\tif ( e.preventDefault ) {\n\t\t\te.preventDefault();\n\t\t}\n\t\t// otherwise set the returnValue property of the original event to false (IE)\n\t\te.returnValue = false;\n\t},\n\tstopPropagation: function() {\n\t\tthis.isPropagationStopped = returnTrue;\n\n\t\tvar e = this.originalEvent;\n\t\tif ( !e ) {\n\t\t\treturn;\n\t\t}\n\t\t// if stopPropagation exists run it on the original event\n\t\tif ( e.stopPropagation ) {\n\t\t\te.stopPropagation();\n\t\t}\n\t\t// otherwise set the cancelBubble property of the original event to true (IE)\n\t\te.cancelBubble = true;\n\t},\n\tstopImmediatePropagation: function() {\n\t\tthis.isImmediatePropagationStopped = returnTrue;\n\t\tthis.stopPropagation();\n\t},\n\tisDefaultPrevented: returnFalse,\n\tisPropagationStopped: returnFalse,\n\tisImmediatePropagationStopped: returnFalse\n};\n\n// Checks if an event happened on an element within another element\n// Used in jQuery.event.special.mouseenter and mouseleave handlers\nvar withinElement = function( event ) {\n\t// Check if mouse(over|out) are still within the same parent element\n\tvar parent = event.relatedTarget;\n\n\t// Firefox sometimes assigns relatedTarget a XUL element\n\t// which we cannot access the parentNode property of\n\ttry {\n\t\t// Traverse up the tree\n\t\twhile ( parent && parent !== this ) {\n\t\t\tparent = parent.parentNode;\n\t\t}\n\n\t\tif ( parent !== this ) {\n\t\t\t// set the correct event type\n\t\t\tevent.type = event.data;\n\n\t\t\t// handle event if we actually just moused on to a non sub-element\n\t\t\tjQuery.event.handle.apply( this, arguments );\n\t\t}\n\n\t// assuming we've left the element since we most likely mousedover a xul element\n\t} catch(e) { }\n},\n\n// In case of event delegation, we only need to rename the event.type,\n// liveHandler will take care of the rest.\ndelegate = function( event ) {\n\tevent.type = event.data;\n\tjQuery.event.handle.apply( this, arguments );\n};\n\n// Create mouseenter and mouseleave events\njQuery.each({\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\"\n}, function( orig, fix ) {\n\tjQuery.event.special[ orig ] = {\n\t\tsetup: function( data ) {\n\t\t\tjQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );\n\t\t},\n\t\tteardown: function( data ) {\n\t\t\tjQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );\n\t\t}\n\t};\n});\n\n// submit delegation\nif ( !jQuery.support.submitBubbles ) {\n\n\tjQuery.event.special.submit = {\n\t\tsetup: function( data, namespaces ) {\n\t\t\tif ( this.nodeName.toLowerCase() !== \"form\" ) {\n\t\t\t\tjQuery.event.add(this, \"click.specialSubmit\", function( e ) {\n\t\t\t\t\tvar elem = e.target, type = elem.type;\n\n\t\t\t\t\tif ( (type === \"submit\" || type === \"image\") && jQuery( elem ).closest(\"form\").length ) {\n\t\t\t\t\t\treturn trigger( \"submit\", this, arguments );\n\t\t\t\t\t}\n\t\t\t\t});\n\t \n\t\t\t\tjQuery.event.add(this, \"keypress.specialSubmit\", function( e ) {\n\t\t\t\t\tvar elem = e.target, type = elem.type;\n\n\t\t\t\t\tif ( (type === \"text\" || type === \"password\") && jQuery( elem ).closest(\"form\").length && e.keyCode === 13 ) {\n\t\t\t\t\t\treturn trigger( \"submit\", this, arguments );\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\n\t\tteardown: function( namespaces ) {\n\t\t\tjQuery.event.remove( this, \".specialSubmit\" );\n\t\t}\n\t};\n\n}\n\n// change delegation, happens here so we have bind.\nif ( !jQuery.support.changeBubbles ) {\n\n\tvar formElems = /textarea|input|select/i,\n\n\tchangeFilters,\n\n\tgetVal = function( elem ) {\n\t\tvar type = elem.type, val = elem.value;\n\n\t\tif ( type === \"radio\" || type === \"checkbox\" ) {\n\t\t\tval = elem.checked;\n\n\t\t} else if ( type === \"select-multiple\" ) {\n\t\t\tval = elem.selectedIndex > -1 ?\n\t\t\t\tjQuery.map( elem.options, function( elem ) {\n\t\t\t\t\treturn elem.selected;\n\t\t\t\t}).join(\"-\") :\n\t\t\t\t\"\";\n\n\t\t} else if ( elem.nodeName.toLowerCase() === \"select\" ) {\n\t\t\tval = elem.selectedIndex;\n\t\t}\n\n\t\treturn val;\n\t},\n\n\ttestChange = function testChange( e ) {\n\t\tvar elem = e.target, data, val;\n\n\t\tif ( !formElems.test( elem.nodeName ) || elem.readOnly ) {\n\t\t\treturn;\n\t\t}\n\n\t\tdata = jQuery.data( elem, \"_change_data\" );\n\t\tval = getVal(elem);\n\n\t\t// the current data will be also retrieved by beforeactivate\n\t\tif ( e.type !== \"focusout\" || elem.type !== \"radio\" ) {\n\t\t\tjQuery.data( elem, \"_change_data\", val );\n\t\t}\n\t\t\n\t\tif ( data === undefined || val === data ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( data != null || val ) {\n\t\t\te.type = \"change\";\n\t\t\treturn jQuery.event.trigger( e, arguments[1], elem );\n\t\t}\n\t};\n\n\tjQuery.event.special.change = {\n\t\tfilters: {\n\t\t\tfocusout: testChange, \n\n\t\t\tclick: function( e ) {\n\t\t\t\tvar elem = e.target, type = elem.type;\n\n\t\t\t\tif ( type === \"radio\" || type === \"checkbox\" || elem.nodeName.toLowerCase() === \"select\" ) {\n\t\t\t\t\treturn testChange.call( this, e );\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// Change has to be called before submit\n\t\t\t// Keydown will be called before keypress, which is used in submit-event delegation\n\t\t\tkeydown: function( e ) {\n\t\t\t\tvar elem = e.target, type = elem.type;\n\n\t\t\t\tif ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== \"textarea\") ||\n\t\t\t\t\t(e.keyCode === 32 && (type === \"checkbox\" || type === \"radio\")) ||\n\t\t\t\t\ttype === \"select-multiple\" ) {\n\t\t\t\t\treturn testChange.call( this, e );\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// Beforeactivate happens also before the previous element is blurred\n\t\t\t// with this event you can't trigger a change event, but you can store\n\t\t\t// information/focus[in] is not needed anymore\n\t\t\tbeforeactivate: function( e ) {\n\t\t\t\tvar elem = e.target;\n\t\t\t\tjQuery.data( elem, \"_change_data\", getVal(elem) );\n\t\t\t}\n\t\t},\n\n\t\tsetup: function( data, namespaces ) {\n\t\t\tif ( this.type === \"file\" ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfor ( var type in changeFilters ) {\n\t\t\t\tjQuery.event.add( this, type + \".specialChange\", changeFilters[type] );\n\t\t\t}\n\n\t\t\treturn formElems.test( this.nodeName );\n\t\t},\n\n\t\tteardown: function( namespaces ) {\n\t\t\tjQuery.event.remove( this, \".specialChange\" );\n\n\t\t\treturn formElems.test( this.nodeName );\n\t\t}\n\t};\n\n\tchangeFilters = jQuery.event.special.change.filters;\n}\n\nfunction trigger( type, elem, args ) {\n\targs[0].type = type;\n\treturn jQuery.event.handle.apply( elem, args );\n}\n\n// Create \"bubbling\" focus and blur events\nif ( document.addEventListener ) {\n\tjQuery.each({ focus: \"focusin\", blur: \"focusout\" }, function( orig, fix ) {\n\t\tjQuery.event.special[ fix ] = {\n\t\t\tsetup: function() {\n\t\t\t\tthis.addEventListener( orig, handler, true );\n\t\t\t}, \n\t\t\tteardown: function() { \n\t\t\t\tthis.removeEventListener( orig, handler, true );\n\t\t\t}\n\t\t};\n\n\t\tfunction handler( e ) { \n\t\t\te = jQuery.event.fix( e );\n\t\t\te.type = fix;\n\t\t\treturn jQuery.event.handle.call( this, e );\n\t\t}\n\t});\n}\n\njQuery.each([\"bind\", \"one\"], function( i, name ) {\n\tjQuery.fn[ name ] = function( type, data, fn ) {\n\t\t// Handle object literals\n\t\tif ( typeof type === \"object\" ) {\n\t\t\tfor ( var key in type ) {\n\t\t\t\tthis[ name ](key, data, type[key], fn);\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\t\t\n\t\tif ( jQuery.isFunction( data ) ) {\n\t\t\tfn = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\tvar handler = name === \"one\" ? jQuery.proxy( fn, function( event ) {\n\t\t\tjQuery( this ).unbind( event, handler );\n\t\t\treturn fn.apply( this, arguments );\n\t\t}) : fn;\n\n\t\tif ( type === \"unload\" && name !== \"one\" ) {\n\t\t\tthis.one( type, data, fn );\n\n\t\t} else {\n\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\tjQuery.event.add( this[i], type, handler, data );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t};\n});\n\njQuery.fn.extend({\n\tunbind: function( type, fn ) {\n\t\t// Handle object literals\n\t\tif ( typeof type === \"object\" && !type.preventDefault ) {\n\t\t\tfor ( var key in type ) {\n\t\t\t\tthis.unbind(key, type[key]);\n\t\t\t}\n\n\t\t} else {\n\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\tjQuery.event.remove( this[i], type, fn );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\t\n\tdelegate: function( selector, types, data, fn ) {\n\t\treturn this.live( types, data, fn, selector );\n\t},\n\t\n\tundelegate: function( selector, types, fn ) {\n\t\tif ( arguments.length === 0 ) {\n\t\t\t\treturn this.unbind( \"live\" );\n\t\t\n\t\t} else {\n\t\t\treturn this.die( types, null, fn, selector );\n\t\t}\n\t},\n\t\n\ttrigger: function( type, data ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.event.trigger( type, data, this );\n\t\t});\n\t},\n\n\ttriggerHandler: function( type, data ) {\n\t\tif ( this[0] ) {\n\t\t\tvar event = jQuery.Event( type );\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t\tjQuery.event.trigger( event, data, this[0] );\n\t\t\treturn event.result;\n\t\t}\n\t},\n\n\ttoggle: function( fn ) {\n\t\t// Save reference to arguments for access in closure\n\t\tvar args = arguments, i = 1;\n\n\t\t// link all the functions, so any of them can unbind this click handler\n\t\twhile ( i < args.length ) {\n\t\t\tjQuery.proxy( fn, args[ i++ ] );\n\t\t}\n\n\t\treturn this.click( jQuery.proxy( fn, function( event ) {\n\t\t\t// Figure out which function to execute\n\t\t\tvar lastToggle = ( jQuery.data( this, \"lastToggle\" + fn.guid ) || 0 ) % i;\n\t\t\tjQuery.data( this, \"lastToggle\" + fn.guid, lastToggle + 1 );\n\n\t\t\t// Make sure that clicks stop\n\t\t\tevent.preventDefault();\n\n\t\t\t// and execute the function\n\t\t\treturn args[ lastToggle ].apply( this, arguments ) || false;\n\t\t}));\n\t},\n\n\thover: function( fnOver, fnOut ) {\n\t\treturn this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );\n\t}\n});\n\nvar liveMap = {\n\tfocus: \"focusin\",\n\tblur: \"focusout\",\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\"\n};\n\njQuery.each([\"live\", \"die\"], function( i, name ) {\n\tjQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {\n\t\tvar type, i = 0, match, namespaces, preType,\n\t\t\tselector = origSelector || this.selector,\n\t\t\tcontext = origSelector ? this : jQuery( this.context );\n\n\t\tif ( jQuery.isFunction( data ) ) {\n\t\t\tfn = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\ttypes = (types || \"\").split(\" \");\n\n\t\twhile ( (type = types[ i++ ]) != null ) {\n\t\t\tmatch = rnamespaces.exec( type );\n\t\t\tnamespaces = \"\";\n\n\t\t\tif ( match )  {\n\t\t\t\tnamespaces = match[0];\n\t\t\t\ttype = type.replace( rnamespaces, \"\" );\n\t\t\t}\n\n\t\t\tif ( type === \"hover\" ) {\n\t\t\t\ttypes.push( \"mouseenter\" + namespaces, \"mouseleave\" + namespaces );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpreType = type;\n\n\t\t\tif ( type === \"focus\" || type === \"blur\" ) {\n\t\t\t\ttypes.push( liveMap[ type ] + namespaces );\n\t\t\t\ttype = type + namespaces;\n\n\t\t\t} else {\n\t\t\t\ttype = (liveMap[ type ] || type) + namespaces;\n\t\t\t}\n\n\t\t\tif ( name === \"live\" ) {\n\t\t\t\t// bind live handler\n\t\t\t\tcontext.each(function(){\n\t\t\t\t\tjQuery.event.add( this, liveConvert( type, selector ),\n\t\t\t\t\t\t{ data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );\n\t\t\t\t});\n\n\t\t\t} else {\n\t\t\t\t// unbind live handler\n\t\t\t\tcontext.unbind( liveConvert( type, selector ), fn );\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn this;\n\t}\n});\n\nfunction liveHandler( event ) {\n\tvar stop, elems = [], selectors = [], args = arguments,\n\t\trelated, match, handleObj, elem, j, i, l, data,\n\t\tevents = jQuery.data( this, \"events\" );\n\n\t// Make sure we avoid non-left-click bubbling in Firefox (#3861)\n\tif ( event.liveFired === this || !events || !events.live || event.button && event.type === \"click\" ) {\n\t\treturn;\n\t}\n\n\tevent.liveFired = this;\n\n\tvar live = events.live.slice(0);\n\n\tfor ( j = 0; j < live.length; j++ ) {\n\t\thandleObj = live[j];\n\n\t\tif ( handleObj.origType.replace( rnamespaces, \"\" ) === event.type ) {\n\t\t\tselectors.push( handleObj.selector );\n\n\t\t} else {\n\t\t\tlive.splice( j--, 1 );\n\t\t}\n\t}\n\n\tmatch = jQuery( event.target ).closest( selectors, event.currentTarget );\n\n\tfor ( i = 0, l = match.length; i < l; i++ ) {\n\t\tfor ( j = 0; j < live.length; j++ ) {\n\t\t\thandleObj = live[j];\n\n\t\t\tif ( match[i].selector === handleObj.selector ) {\n\t\t\t\telem = match[i].elem;\n\t\t\t\trelated = null;\n\n\t\t\t\t// Those two events require additional checking\n\t\t\t\tif ( handleObj.preType === \"mouseenter\" || handleObj.preType === \"mouseleave\" ) {\n\t\t\t\t\trelated = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];\n\t\t\t\t}\n\n\t\t\t\tif ( !related || related !== elem ) {\n\t\t\t\t\telems.push({ elem: elem, handleObj: handleObj });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor ( i = 0, l = elems.length; i < l; i++ ) {\n\t\tmatch = elems[i];\n\t\tevent.currentTarget = match.elem;\n\t\tevent.data = match.handleObj.data;\n\t\tevent.handleObj = match.handleObj;\n\n\t\tif ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {\n\t\t\tstop = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn stop;\n}\n\nfunction liveConvert( type, selector ) {\n\treturn \"live.\" + (type && type !== \"*\" ? type + \".\" : \"\") + selector.replace(/\\./g, \"`\").replace(/ /g, \"&\");\n}\n\njQuery.each( (\"blur focus focusin focusout load resize scroll unload click dblclick \" +\n\t\"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave \" +\n\t\"change select submit keydown keypress keyup error\").split(\" \"), function( i, name ) {\n\n\t// Handle event binding\n\tjQuery.fn[ name ] = function( fn ) {\n\t\treturn fn ? this.bind( name, fn ) : this.trigger( name );\n\t};\n\n\tif ( jQuery.attrFn ) {\n\t\tjQuery.attrFn[ name ] = true;\n\t}\n});\n\n// Prevent memory leaks in IE\n// Window isn't included so as not to unbind existing unload events\n// More info:\n//  - http://isaacschlueter.com/2006/10/msie-memory-leaks/\nif ( window.attachEvent && !window.addEventListener ) {\n\twindow.attachEvent(\"onunload\", function() {\n\t\tfor ( var id in jQuery.cache ) {\n\t\t\tif ( jQuery.cache[ id ].handle ) {\n\t\t\t\t// Try/Catch is to handle iframes being unloaded, see #4280\n\t\t\t\ttry {\n\t\t\t\t\tjQuery.event.remove( jQuery.cache[ id ].handle.elem );\n\t\t\t\t} catch(e) {}\n\t\t\t}\n\t\t}\n\t});\n}\n/*!\n * Sizzle CSS Selector Engine - v1.0\n *  Copyright 2009, The Dojo Foundation\n *  Released under the MIT, BSD, and GPL Licenses.\n *  More information: http://sizzlejs.com/\n */\n(function(){\n\nvar chunker = /((?:\\((?:\\([^()]+\\)|[^()]+)+\\)|\\[(?:\\[[^[\\]]*\\]|['\"][^'\"]*['\"]|[^[\\]'\"]+)+\\]|\\\\.|[^ >+~,(\\[\\\\]+)+|[>+~])(\\s*,\\s*)?((?:.|\\r|\\n)*)/g,\n\tdone = 0,\n\ttoString = Object.prototype.toString,\n\thasDuplicate = false,\n\tbaseHasDuplicate = true;\n\n// Here we check if the JavaScript engine is using some sort of\n// optimization where it does not always call our comparision\n// function. If that is the case, discard the hasDuplicate value.\n//   Thus far that includes Google Chrome.\n[0, 0].sort(function(){\n\tbaseHasDuplicate = false;\n\treturn 0;\n});\n\nvar Sizzle = function(selector, context, results, seed) {\n\tresults = results || [];\n\tvar origContext = context = context || document;\n\n\tif ( context.nodeType !== 1 && context.nodeType !== 9 ) {\n\t\treturn [];\n\t}\n\t\n\tif ( !selector || typeof selector !== \"string\" ) {\n\t\treturn results;\n\t}\n\n\tvar parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),\n\t\tsoFar = selector;\n\t\n\t// Reset the position of the chunker regexp (start from head)\n\twhile ( (chunker.exec(\"\"), m = chunker.exec(soFar)) !== null ) {\n\t\tsoFar = m[3];\n\t\t\n\t\tparts.push( m[1] );\n\t\t\n\t\tif ( m[2] ) {\n\t\t\textra = m[3];\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif ( parts.length > 1 && origPOS.exec( selector ) ) {\n\t\tif ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\n\t\t\tset = posProcess( parts[0] + parts[1], context );\n\t\t} else {\n\t\t\tset = Expr.relative[ parts[0] ] ?\n\t\t\t\t[ context ] :\n\t\t\t\tSizzle( parts.shift(), context );\n\n\t\t\twhile ( parts.length ) {\n\t\t\t\tselector = parts.shift();\n\n\t\t\t\tif ( Expr.relative[ selector ] ) {\n\t\t\t\t\tselector += parts.shift();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tset = posProcess( selector, set );\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// Take a shortcut and set the context if the root selector is an ID\n\t\t// (but not if it'll be faster if the inner selector is an ID)\n\t\tif ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\n\t\t\t\tExpr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\n\t\t\tvar ret = Sizzle.find( parts.shift(), context, contextXML );\n\t\t\tcontext = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];\n\t\t}\n\n\t\tif ( context ) {\n\t\t\tvar ret = seed ?\n\t\t\t\t{ expr: parts.pop(), set: makeArray(seed) } :\n\t\t\t\tSizzle.find( parts.pop(), parts.length === 1 && (parts[0] === \"~\" || parts[0] === \"+\") && context.parentNode ? context.parentNode : context, contextXML );\n\t\t\tset = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;\n\n\t\t\tif ( parts.length > 0 ) {\n\t\t\t\tcheckSet = makeArray(set);\n\t\t\t} else {\n\t\t\t\tprune = false;\n\t\t\t}\n\n\t\t\twhile ( parts.length ) {\n\t\t\t\tvar cur = parts.pop(), pop = cur;\n\n\t\t\t\tif ( !Expr.relative[ cur ] ) {\n\t\t\t\t\tcur = \"\";\n\t\t\t\t} else {\n\t\t\t\t\tpop = parts.pop();\n\t\t\t\t}\n\n\t\t\t\tif ( pop == null ) {\n\t\t\t\t\tpop = context;\n\t\t\t\t}\n\n\t\t\t\tExpr.relative[ cur ]( checkSet, pop, contextXML );\n\t\t\t}\n\t\t} else {\n\t\t\tcheckSet = parts = [];\n\t\t}\n\t}\n\n\tif ( !checkSet ) {\n\t\tcheckSet = set;\n\t}\n\n\tif ( !checkSet ) {\n\t\tSizzle.error( cur || selector );\n\t}\n\n\tif ( toString.call(checkSet) === \"[object Array]\" ) {\n\t\tif ( !prune ) {\n\t\t\tresults.push.apply( results, checkSet );\n\t\t} else if ( context && context.nodeType === 1 ) {\n\t\t\tfor ( var i = 0; checkSet[i] != null; i++ ) {\n\t\t\t\tif ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {\n\t\t\t\t\tresults.push( set[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor ( var i = 0; checkSet[i] != null; i++ ) {\n\t\t\t\tif ( checkSet[i] && checkSet[i].nodeType === 1 ) {\n\t\t\t\t\tresults.push( set[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tmakeArray( checkSet, results );\n\t}\n\n\tif ( extra ) {\n\t\tSizzle( extra, origContext, results, seed );\n\t\tSizzle.uniqueSort( results );\n\t}\n\n\treturn results;\n};\n\nSizzle.uniqueSort = function(results){\n\tif ( sortOrder ) {\n\t\thasDuplicate = baseHasDuplicate;\n\t\tresults.sort(sortOrder);\n\n\t\tif ( hasDuplicate ) {\n\t\t\tfor ( var i = 1; i < results.length; i++ ) {\n\t\t\t\tif ( results[i] === results[i-1] ) {\n\t\t\t\t\tresults.splice(i--, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n};\n\nSizzle.matches = function(expr, set){\n\treturn Sizzle(expr, null, null, set);\n};\n\nSizzle.find = function(expr, context, isXML){\n\tvar set, match;\n\n\tif ( !expr ) {\n\t\treturn [];\n\t}\n\n\tfor ( var i = 0, l = Expr.order.length; i < l; i++ ) {\n\t\tvar type = Expr.order[i], match;\n\t\t\n\t\tif ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\n\t\t\tvar left = match[1];\n\t\t\tmatch.splice(1,1);\n\n\t\t\tif ( left.substr( left.length - 1 ) !== \"\\\\\" ) {\n\t\t\t\tmatch[1] = (match[1] || \"\").replace(/\\\\/g, \"\");\n\t\t\t\tset = Expr.find[ type ]( match, context, isXML );\n\t\t\t\tif ( set != null ) {\n\t\t\t\t\texpr = expr.replace( Expr.match[ type ], \"\" );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( !set ) {\n\t\tset = context.getElementsByTagName(\"*\");\n\t}\n\n\treturn {set: set, expr: expr};\n};\n\nSizzle.filter = function(expr, set, inplace, not){\n\tvar old = expr, result = [], curLoop = set, match, anyFound,\n\t\tisXMLFilter = set && set[0] && isXML(set[0]);\n\n\twhile ( expr && set.length ) {\n\t\tfor ( var type in Expr.filter ) {\n\t\t\tif ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {\n\t\t\t\tvar filter = Expr.filter[ type ], found, item, left = match[1];\n\t\t\t\tanyFound = false;\n\n\t\t\t\tmatch.splice(1,1);\n\n\t\t\t\tif ( left.substr( left.length - 1 ) === \"\\\\\" ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif ( curLoop === result ) {\n\t\t\t\t\tresult = [];\n\t\t\t\t}\n\n\t\t\t\tif ( Expr.preFilter[ type ] ) {\n\t\t\t\t\tmatch = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\n\n\t\t\t\t\tif ( !match ) {\n\t\t\t\t\t\tanyFound = found = true;\n\t\t\t\t\t} else if ( match === true ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( match ) {\n\t\t\t\t\tfor ( var i = 0; (item = curLoop[i]) != null; i++ ) {\n\t\t\t\t\t\tif ( item ) {\n\t\t\t\t\t\t\tfound = filter( item, match, i, curLoop );\n\t\t\t\t\t\t\tvar pass = not ^ !!found;\n\n\t\t\t\t\t\t\tif ( inplace && found != null ) {\n\t\t\t\t\t\t\t\tif ( pass ) {\n\t\t\t\t\t\t\t\t\tanyFound = true;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcurLoop[i] = false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if ( pass ) {\n\t\t\t\t\t\t\t\tresult.push( item );\n\t\t\t\t\t\t\t\tanyFound = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( found !== undefined ) {\n\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\tcurLoop = result;\n\t\t\t\t\t}\n\n\t\t\t\t\texpr = expr.replace( Expr.match[ type ], \"\" );\n\n\t\t\t\t\tif ( !anyFound ) {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Improper expression\n\t\tif ( expr === old ) {\n\t\t\tif ( anyFound == null ) {\n\t\t\t\tSizzle.error( expr );\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\told = expr;\n\t}\n\n\treturn curLoop;\n};\n\nSizzle.error = function( msg ) {\n\tthrow \"Syntax error, unrecognized expression: \" + msg;\n};\n\nvar Expr = Sizzle.selectors = {\n\torder: [ \"ID\", \"NAME\", \"TAG\" ],\n\tmatch: {\n\t\tID: /#((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)/,\n\t\tCLASS: /\\.((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)/,\n\t\tNAME: /\\[name=['\"]*((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)['\"]*\\]/,\n\t\tATTR: /\\[\\s*((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)\\s*(?:(\\S?=)\\s*(['\"]*)(.*?)\\3|)\\s*\\]/,\n\t\tTAG: /^((?:[\\w\\u00c0-\\uFFFF\\*-]|\\\\.)+)/,\n\t\tCHILD: /:(only|nth|last|first)-child(?:\\((even|odd|[\\dn+-]*)\\))?/,\n\t\tPOS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\))?(?=[^-]|$)/,\n\t\tPSEUDO: /:((?:[\\w\\u00c0-\\uFFFF-]|\\\\.)+)(?:\\((['\"]?)((?:\\([^\\)]+\\)|[^\\(\\)]*)+)\\2\\))?/\n\t},\n\tleftMatch: {},\n\tattrMap: {\n\t\t\"class\": \"className\",\n\t\t\"for\": \"htmlFor\"\n\t},\n\tattrHandle: {\n\t\thref: function(elem){\n\t\t\treturn elem.getAttribute(\"href\");\n\t\t}\n\t},\n\trelative: {\n\t\t\"+\": function(checkSet, part){\n\t\t\tvar isPartStr = typeof part === \"string\",\n\t\t\t\tisTag = isPartStr && !/\\W/.test(part),\n\t\t\t\tisPartStrNotTag = isPartStr && !isTag;\n\n\t\t\tif ( isTag ) {\n\t\t\t\tpart = part.toLowerCase();\n\t\t\t}\n\n\t\t\tfor ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\n\t\t\t\tif ( (elem = checkSet[i]) ) {\n\t\t\t\t\twhile ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\n\n\t\t\t\t\tcheckSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?\n\t\t\t\t\t\telem || false :\n\t\t\t\t\t\telem === part;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( isPartStrNotTag ) {\n\t\t\t\tSizzle.filter( part, checkSet, true );\n\t\t\t}\n\t\t},\n\t\t\">\": function(checkSet, part){\n\t\t\tvar isPartStr = typeof part === \"string\";\n\n\t\t\tif ( isPartStr && !/\\W/.test(part) ) {\n\t\t\t\tpart = part.toLowerCase();\n\n\t\t\t\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\t\t\t\tvar elem = checkSet[i];\n\t\t\t\t\tif ( elem ) {\n\t\t\t\t\t\tvar parent = elem.parentNode;\n\t\t\t\t\t\tcheckSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\t\t\t\tvar elem = checkSet[i];\n\t\t\t\t\tif ( elem ) {\n\t\t\t\t\t\tcheckSet[i] = isPartStr ?\n\t\t\t\t\t\t\telem.parentNode :\n\t\t\t\t\t\t\telem.parentNode === part;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( isPartStr ) {\n\t\t\t\t\tSizzle.filter( part, checkSet, true );\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t\"\": function(checkSet, part, isXML){\n\t\t\tvar doneName = done++, checkFn = dirCheck;\n\n\t\t\tif ( typeof part === \"string\" && !/\\W/.test(part) ) {\n\t\t\t\tvar nodeCheck = part = part.toLowerCase();\n\t\t\t\tcheckFn = dirNodeCheck;\n\t\t\t}\n\n\t\t\tcheckFn(\"parentNode\", part, doneName, checkSet, nodeCheck, isXML);\n\t\t},\n\t\t\"~\": function(checkSet, part, isXML){\n\t\t\tvar doneName = done++, checkFn = dirCheck;\n\n\t\t\tif ( typeof part === \"string\" && !/\\W/.test(part) ) {\n\t\t\t\tvar nodeCheck = part = part.toLowerCase();\n\t\t\t\tcheckFn = dirNodeCheck;\n\t\t\t}\n\n\t\t\tcheckFn(\"previousSibling\", part, doneName, checkSet, nodeCheck, isXML);\n\t\t}\n\t},\n\tfind: {\n\t\tID: function(match, context, isXML){\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && !isXML ) {\n\t\t\t\tvar m = context.getElementById(match[1]);\n\t\t\t\treturn m ? [m] : [];\n\t\t\t}\n\t\t},\n\t\tNAME: function(match, context){\n\t\t\tif ( typeof context.getElementsByName !== \"undefined\" ) {\n\t\t\t\tvar ret = [], results = context.getElementsByName(match[1]);\n\n\t\t\t\tfor ( var i = 0, l = results.length; i < l; i++ ) {\n\t\t\t\t\tif ( results[i].getAttribute(\"name\") === match[1] ) {\n\t\t\t\t\t\tret.push( results[i] );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn ret.length === 0 ? null : ret;\n\t\t\t}\n\t\t},\n\t\tTAG: function(match, context){\n\t\t\treturn context.getElementsByTagName(match[1]);\n\t\t}\n\t},\n\tpreFilter: {\n\t\tCLASS: function(match, curLoop, inplace, result, not, isXML){\n\t\t\tmatch = \" \" + match[1].replace(/\\\\/g, \"\") + \" \";\n\n\t\t\tif ( isXML ) {\n\t\t\t\treturn match;\n\t\t\t}\n\n\t\t\tfor ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\n\t\t\t\tif ( elem ) {\n\t\t\t\t\tif ( not ^ (elem.className && (\" \" + elem.className + \" \").replace(/[\\t\\n]/g, \" \").indexOf(match) >= 0) ) {\n\t\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\t\tresult.push( elem );\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if ( inplace ) {\n\t\t\t\t\t\tcurLoop[i] = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t},\n\t\tID: function(match){\n\t\t\treturn match[1].replace(/\\\\/g, \"\");\n\t\t},\n\t\tTAG: function(match, curLoop){\n\t\t\treturn match[1].toLowerCase();\n\t\t},\n\t\tCHILD: function(match){\n\t\t\tif ( match[1] === \"nth\" ) {\n\t\t\t\t// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\n\t\t\t\tvar test = /(-?)(\\d*)n((?:\\+|-)?\\d*)/.exec(\n\t\t\t\t\tmatch[2] === \"even\" && \"2n\" || match[2] === \"odd\" && \"2n+1\" ||\n\t\t\t\t\t!/\\D/.test( match[2] ) && \"0n+\" + match[2] || match[2]);\n\n\t\t\t\t// calculate the numbers (first)n+(last) including if they are negative\n\t\t\t\tmatch[2] = (test[1] + (test[2] || 1)) - 0;\n\t\t\t\tmatch[3] = test[3] - 0;\n\t\t\t}\n\n\t\t\t// TODO: Move to normal caching system\n\t\t\tmatch[0] = done++;\n\n\t\t\treturn match;\n\t\t},\n\t\tATTR: function(match, curLoop, inplace, result, not, isXML){\n\t\t\tvar name = match[1].replace(/\\\\/g, \"\");\n\t\t\t\n\t\t\tif ( !isXML && Expr.attrMap[name] ) {\n\t\t\t\tmatch[1] = Expr.attrMap[name];\n\t\t\t}\n\n\t\t\tif ( match[2] === \"~=\" ) {\n\t\t\t\tmatch[4] = \" \" + match[4] + \" \";\n\t\t\t}\n\n\t\t\treturn match;\n\t\t},\n\t\tPSEUDO: function(match, curLoop, inplace, result, not){\n\t\t\tif ( match[1] === \"not\" ) {\n\t\t\t\t// If we're dealing with a complex expression, or a simple one\n\t\t\t\tif ( ( chunker.exec(match[3]) || \"\" ).length > 1 || /^\\w/.test(match[3]) ) {\n\t\t\t\t\tmatch[3] = Sizzle(match[3], null, null, curLoop);\n\t\t\t\t} else {\n\t\t\t\t\tvar ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\n\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\tresult.push.apply( result, ret );\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\treturn match;\n\t\t},\n\t\tPOS: function(match){\n\t\t\tmatch.unshift( true );\n\t\t\treturn match;\n\t\t}\n\t},\n\tfilters: {\n\t\tenabled: function(elem){\n\t\t\treturn elem.disabled === false && elem.type !== \"hidden\";\n\t\t},\n\t\tdisabled: function(elem){\n\t\t\treturn elem.disabled === true;\n\t\t},\n\t\tchecked: function(elem){\n\t\t\treturn elem.checked === true;\n\t\t},\n\t\tselected: function(elem){\n\t\t\t// Accessing this property makes selected-by-default\n\t\t\t// options in Safari work properly\n\t\t\telem.parentNode.selectedIndex;\n\t\t\treturn elem.selected === true;\n\t\t},\n\t\tparent: function(elem){\n\t\t\treturn !!elem.firstChild;\n\t\t},\n\t\tempty: function(elem){\n\t\t\treturn !elem.firstChild;\n\t\t},\n\t\thas: function(elem, i, match){\n\t\t\treturn !!Sizzle( match[3], elem ).length;\n\t\t},\n\t\theader: function(elem){\n\t\t\treturn /h\\d/i.test( elem.nodeName );\n\t\t},\n\t\ttext: function(elem){\n\t\t\treturn \"text\" === elem.type;\n\t\t},\n\t\tradio: function(elem){\n\t\t\treturn \"radio\" === elem.type;\n\t\t},\n\t\tcheckbox: function(elem){\n\t\t\treturn \"checkbox\" === elem.type;\n\t\t},\n\t\tfile: function(elem){\n\t\t\treturn \"file\" === elem.type;\n\t\t},\n\t\tpassword: function(elem){\n\t\t\treturn \"password\" === elem.type;\n\t\t},\n\t\tsubmit: function(elem){\n\t\t\treturn \"submit\" === elem.type;\n\t\t},\n\t\timage: function(elem){\n\t\t\treturn \"image\" === elem.type;\n\t\t},\n\t\treset: function(elem){\n\t\t\treturn \"reset\" === elem.type;\n\t\t},\n\t\tbutton: function(elem){\n\t\t\treturn \"button\" === elem.type || elem.nodeName.toLowerCase() === \"button\";\n\t\t},\n\t\tinput: function(elem){\n\t\t\treturn /input|select|textarea|button/i.test(elem.nodeName);\n\t\t}\n\t},\n\tsetFilters: {\n\t\tfirst: function(elem, i){\n\t\t\treturn i === 0;\n\t\t},\n\t\tlast: function(elem, i, match, array){\n\t\t\treturn i === array.length - 1;\n\t\t},\n\t\teven: function(elem, i){\n\t\t\treturn i % 2 === 0;\n\t\t},\n\t\todd: function(elem, i){\n\t\t\treturn i % 2 === 1;\n\t\t},\n\t\tlt: function(elem, i, match){\n\t\t\treturn i < match[3] - 0;\n\t\t},\n\t\tgt: function(elem, i, match){\n\t\t\treturn i > match[3] - 0;\n\t\t},\n\t\tnth: function(elem, i, match){\n\t\t\treturn match[3] - 0 === i;\n\t\t},\n\t\teq: function(elem, i, match){\n\t\t\treturn match[3] - 0 === i;\n\t\t}\n\t},\n\tfilter: {\n\t\tPSEUDO: function(elem, match, i, array){\n\t\t\tvar name = match[1], filter = Expr.filters[ name ];\n\n\t\t\tif ( filter ) {\n\t\t\t\treturn filter( elem, i, match, array );\n\t\t\t} else if ( name === \"contains\" ) {\n\t\t\t\treturn (elem.textContent || elem.innerText || getText([ elem ]) || \"\").indexOf(match[3]) >= 0;\n\t\t\t} else if ( name === \"not\" ) {\n\t\t\t\tvar not = match[3];\n\n\t\t\t\tfor ( var i = 0, l = not.length; i < l; i++ ) {\n\t\t\t\t\tif ( not[i] === elem ) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\tSizzle.error( \"Syntax error, unrecognized expression: \" + name );\n\t\t\t}\n\t\t},\n\t\tCHILD: function(elem, match){\n\t\t\tvar type = match[1], node = elem;\n\t\t\tswitch (type) {\n\t\t\t\tcase 'only':\n\t\t\t\tcase 'first':\n\t\t\t\t\twhile ( (node = node.previousSibling) )\t {\n\t\t\t\t\t\tif ( node.nodeType === 1 ) { \n\t\t\t\t\t\t\treturn false; \n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif ( type === \"first\" ) { \n\t\t\t\t\t\treturn true; \n\t\t\t\t\t}\n\t\t\t\t\tnode = elem;\n\t\t\t\tcase 'last':\n\t\t\t\t\twhile ( (node = node.nextSibling) )\t {\n\t\t\t\t\t\tif ( node.nodeType === 1 ) { \n\t\t\t\t\t\t\treturn false; \n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\tcase 'nth':\n\t\t\t\t\tvar first = match[2], last = match[3];\n\n\t\t\t\t\tif ( first === 1 && last === 0 ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tvar doneName = match[0],\n\t\t\t\t\t\tparent = elem.parentNode;\n\t\n\t\t\t\t\tif ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\n\t\t\t\t\t\tvar count = 0;\n\t\t\t\t\t\tfor ( node = parent.firstChild; node; node = node.nextSibling ) {\n\t\t\t\t\t\t\tif ( node.nodeType === 1 ) {\n\t\t\t\t\t\t\t\tnode.nodeIndex = ++count;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} \n\t\t\t\t\t\tparent.sizcache = doneName;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tvar diff = elem.nodeIndex - last;\n\t\t\t\t\tif ( first === 0 ) {\n\t\t\t\t\t\treturn diff === 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn ( diff % first === 0 && diff / first >= 0 );\n\t\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tID: function(elem, match){\n\t\t\treturn elem.nodeType === 1 && elem.getAttribute(\"id\") === match;\n\t\t},\n\t\tTAG: function(elem, match){\n\t\t\treturn (match === \"*\" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;\n\t\t},\n\t\tCLASS: function(elem, match){\n\t\t\treturn (\" \" + (elem.className || elem.getAttribute(\"class\")) + \" \")\n\t\t\t\t.indexOf( match ) > -1;\n\t\t},\n\t\tATTR: function(elem, match){\n\t\t\tvar name = match[1],\n\t\t\t\tresult = Expr.attrHandle[ name ] ?\n\t\t\t\t\tExpr.attrHandle[ name ]( elem ) :\n\t\t\t\t\telem[ name ] != null ?\n\t\t\t\t\t\telem[ name ] :\n\t\t\t\t\t\telem.getAttribute( name ),\n\t\t\t\tvalue = result + \"\",\n\t\t\t\ttype = match[2],\n\t\t\t\tcheck = match[4];\n\n\t\t\treturn result == null ?\n\t\t\t\ttype === \"!=\" :\n\t\t\t\ttype === \"=\" ?\n\t\t\t\tvalue === check :\n\t\t\t\ttype === \"*=\" ?\n\t\t\t\tvalue.indexOf(check) >= 0 :\n\t\t\t\ttype === \"~=\" ?\n\t\t\t\t(\" \" + value + \" \").indexOf(check) >= 0 :\n\t\t\t\t!check ?\n\t\t\t\tvalue && result !== false :\n\t\t\t\ttype === \"!=\" ?\n\t\t\t\tvalue !== check :\n\t\t\t\ttype === \"^=\" ?\n\t\t\t\tvalue.indexOf(check) === 0 :\n\t\t\t\ttype === \"$=\" ?\n\t\t\t\tvalue.substr(value.length - check.length) === check :\n\t\t\t\ttype === \"|=\" ?\n\t\t\t\tvalue === check || value.substr(0, check.length + 1) === check + \"-\" :\n\t\t\t\tfalse;\n\t\t},\n\t\tPOS: function(elem, match, i, array){\n\t\t\tvar name = match[2], filter = Expr.setFilters[ name ];\n\n\t\t\tif ( filter ) {\n\t\t\t\treturn filter( elem, i, match, array );\n\t\t\t}\n\t\t}\n\t}\n};\n\nvar origPOS = Expr.match.POS;\n\nfor ( var type in Expr.match ) {\n\tExpr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\\[]*\\])(?![^\\(]*\\))/.source );\n\tExpr.leftMatch[ type ] = new RegExp( /(^(?:.|\\r|\\n)*?)/.source + Expr.match[ type ].source.replace(/\\\\(\\d+)/g, function(all, num){\n\t\treturn \"\\\\\" + (num - 0 + 1);\n\t}));\n}\n\nvar makeArray = function(array, results) {\n\tarray = Array.prototype.slice.call( array, 0 );\n\n\tif ( results ) {\n\t\tresults.push.apply( results, array );\n\t\treturn results;\n\t}\n\t\n\treturn array;\n};\n\n// Perform a simple check to determine if the browser is capable of\n// converting a NodeList to an array using builtin methods.\n// Also verifies that the returned array holds DOM nodes\n// (which is not the case in the Blackberry browser)\ntry {\n\tArray.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;\n\n// Provide a fallback method if it does not work\n} catch(e){\n\tmakeArray = function(array, results) {\n\t\tvar ret = results || [];\n\n\t\tif ( toString.call(array) === \"[object Array]\" ) {\n\t\t\tArray.prototype.push.apply( ret, array );\n\t\t} else {\n\t\t\tif ( typeof array.length === \"number\" ) {\n\t\t\t\tfor ( var i = 0, l = array.length; i < l; i++ ) {\n\t\t\t\t\tret.push( array[i] );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( var i = 0; array[i]; i++ ) {\n\t\t\t\t\tret.push( array[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t};\n}\n\nvar sortOrder;\n\nif ( document.documentElement.compareDocumentPosition ) {\n\tsortOrder = function( a, b ) {\n\t\tif ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\n\t\t\tif ( a == b ) {\n\t\t\t\thasDuplicate = true;\n\t\t\t}\n\t\t\treturn a.compareDocumentPosition ? -1 : 1;\n\t\t}\n\n\t\tvar ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;\n\t\tif ( ret === 0 ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn ret;\n\t};\n} else if ( \"sourceIndex\" in document.documentElement ) {\n\tsortOrder = function( a, b ) {\n\t\tif ( !a.sourceIndex || !b.sourceIndex ) {\n\t\t\tif ( a == b ) {\n\t\t\t\thasDuplicate = true;\n\t\t\t}\n\t\t\treturn a.sourceIndex ? -1 : 1;\n\t\t}\n\n\t\tvar ret = a.sourceIndex - b.sourceIndex;\n\t\tif ( ret === 0 ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn ret;\n\t};\n} else if ( document.createRange ) {\n\tsortOrder = function( a, b ) {\n\t\tif ( !a.ownerDocument || !b.ownerDocument ) {\n\t\t\tif ( a == b ) {\n\t\t\t\thasDuplicate = true;\n\t\t\t}\n\t\t\treturn a.ownerDocument ? -1 : 1;\n\t\t}\n\n\t\tvar aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();\n\t\taRange.setStart(a, 0);\n\t\taRange.setEnd(a, 0);\n\t\tbRange.setStart(b, 0);\n\t\tbRange.setEnd(b, 0);\n\t\tvar ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);\n\t\tif ( ret === 0 ) {\n\t\t\thasDuplicate = true;\n\t\t}\n\t\treturn ret;\n\t};\n}\n\n// Utility function for retreiving the text value of an array of DOM nodes\nfunction getText( elems ) {\n\tvar ret = \"\", elem;\n\n\tfor ( var i = 0; elems[i]; i++ ) {\n\t\telem = elems[i];\n\n\t\t// Get the text from text nodes and CDATA nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 4 ) {\n\t\t\tret += elem.nodeValue;\n\n\t\t// Traverse everything else, except comment nodes\n\t\t} else if ( elem.nodeType !== 8 ) {\n\t\t\tret += getText( elem.childNodes );\n\t\t}\n\t}\n\n\treturn ret;\n}\n\n// Check to see if the browser returns elements by name when\n// querying by getElementById (and provide a workaround)\n(function(){\n\t// We're going to inject a fake input element with a specified name\n\tvar form = document.createElement(\"div\"),\n\t\tid = \"script\" + (new Date).getTime();\n\tform.innerHTML = \"<a name='\" + id + \"'/>\";\n\n\t// Inject it into the root element, check its status, and remove it quickly\n\tvar root = document.documentElement;\n\troot.insertBefore( form, root.firstChild );\n\n\t// The workaround has to do additional checks after a getElementById\n\t// Which slows things down for other browsers (hence the branching)\n\tif ( document.getElementById( id ) ) {\n\t\tExpr.find.ID = function(match, context, isXML){\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && !isXML ) {\n\t\t\t\tvar m = context.getElementById(match[1]);\n\t\t\t\treturn m ? m.id === match[1] || typeof m.getAttributeNode !== \"undefined\" && m.getAttributeNode(\"id\").nodeValue === match[1] ? [m] : undefined : [];\n\t\t\t}\n\t\t};\n\n\t\tExpr.filter.ID = function(elem, match){\n\t\t\tvar node = typeof elem.getAttributeNode !== \"undefined\" && elem.getAttributeNode(\"id\");\n\t\t\treturn elem.nodeType === 1 && node && node.nodeValue === match;\n\t\t};\n\t}\n\n\troot.removeChild( form );\n\troot = form = null; // release memory in IE\n})();\n\n(function(){\n\t// Check to see if the browser returns only elements\n\t// when doing getElementsByTagName(\"*\")\n\n\t// Create a fake element\n\tvar div = document.createElement(\"div\");\n\tdiv.appendChild( document.createComment(\"\") );\n\n\t// Make sure no comments are found\n\tif ( div.getElementsByTagName(\"*\").length > 0 ) {\n\t\tExpr.find.TAG = function(match, context){\n\t\t\tvar results = context.getElementsByTagName(match[1]);\n\n\t\t\t// Filter out possible comments\n\t\t\tif ( match[1] === \"*\" ) {\n\t\t\t\tvar tmp = [];\n\n\t\t\t\tfor ( var i = 0; results[i]; i++ ) {\n\t\t\t\t\tif ( results[i].nodeType === 1 ) {\n\t\t\t\t\t\ttmp.push( results[i] );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresults = tmp;\n\t\t\t}\n\n\t\t\treturn results;\n\t\t};\n\t}\n\n\t// Check to see if an attribute returns normalized href attributes\n\tdiv.innerHTML = \"<a href='#'></a>\";\n\tif ( div.firstChild && typeof div.firstChild.getAttribute !== \"undefined\" &&\n\t\t\tdiv.firstChild.getAttribute(\"href\") !== \"#\" ) {\n\t\tExpr.attrHandle.href = function(elem){\n\t\t\treturn elem.getAttribute(\"href\", 2);\n\t\t};\n\t}\n\n\tdiv = null; // release memory in IE\n})();\n\nif ( document.querySelectorAll ) {\n\t(function(){\n\t\tvar oldSizzle = Sizzle, div = document.createElement(\"div\");\n\t\tdiv.innerHTML = \"<p class='TEST'></p>\";\n\n\t\t// Safari can't handle uppercase or unicode characters when\n\t\t// in quirks mode.\n\t\tif ( div.querySelectorAll && div.querySelectorAll(\".TEST\").length === 0 ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tSizzle = function(query, context, extra, seed){\n\t\t\tcontext = context || document;\n\n\t\t\t// Only use querySelectorAll on non-XML documents\n\t\t\t// (ID selectors don't work in non-HTML documents)\n\t\t\tif ( !seed && context.nodeType === 9 && !isXML(context) ) {\n\t\t\t\ttry {\n\t\t\t\t\treturn makeArray( context.querySelectorAll(query), extra );\n\t\t\t\t} catch(e){}\n\t\t\t}\n\t\t\n\t\t\treturn oldSizzle(query, context, extra, seed);\n\t\t};\n\n\t\tfor ( var prop in oldSizzle ) {\n\t\t\tSizzle[ prop ] = oldSizzle[ prop ];\n\t\t}\n\n\t\tdiv = null; // release memory in IE\n\t})();\n}\n\n(function(){\n\tvar div = document.createElement(\"div\");\n\n\tdiv.innerHTML = \"<div class='test e'></div><div class='test'></div>\";\n\n\t// Opera can't find a second classname (in 9.6)\n\t// Also, make sure that getElementsByClassName actually exists\n\tif ( !div.getElementsByClassName || div.getElementsByClassName(\"e\").length === 0 ) {\n\t\treturn;\n\t}\n\n\t// Safari caches class attributes, doesn't catch changes (in 3.2)\n\tdiv.lastChild.className = \"e\";\n\n\tif ( div.getElementsByClassName(\"e\").length === 1 ) {\n\t\treturn;\n\t}\n\t\n\tExpr.order.splice(1, 0, \"CLASS\");\n\tExpr.find.CLASS = function(match, context, isXML) {\n\t\tif ( typeof context.getElementsByClassName !== \"undefined\" && !isXML ) {\n\t\t\treturn context.getElementsByClassName(match[1]);\n\t\t}\n\t};\n\n\tdiv = null; // release memory in IE\n})();\n\nfunction dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\n\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\tvar elem = checkSet[i];\n\t\tif ( elem ) {\n\t\t\telem = elem[dir];\n\t\t\tvar match = false;\n\n\t\t\twhile ( elem ) {\n\t\t\t\tif ( elem.sizcache === doneName ) {\n\t\t\t\t\tmatch = checkSet[elem.sizset];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeType === 1 && !isXML ){\n\t\t\t\t\telem.sizcache = doneName;\n\t\t\t\t\telem.sizset = i;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeName.toLowerCase() === cur ) {\n\t\t\t\t\tmatch = elem;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\telem = elem[dir];\n\t\t\t}\n\n\t\t\tcheckSet[i] = match;\n\t\t}\n\t}\n}\n\nfunction dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\n\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\tvar elem = checkSet[i];\n\t\tif ( elem ) {\n\t\t\telem = elem[dir];\n\t\t\tvar match = false;\n\n\t\t\twhile ( elem ) {\n\t\t\t\tif ( elem.sizcache === doneName ) {\n\t\t\t\t\tmatch = checkSet[elem.sizset];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\tif ( !isXML ) {\n\t\t\t\t\t\telem.sizcache = doneName;\n\t\t\t\t\t\telem.sizset = i;\n\t\t\t\t\t}\n\t\t\t\t\tif ( typeof cur !== \"string\" ) {\n\t\t\t\t\t\tif ( elem === cur ) {\n\t\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\n\t\t\t\t\t\tmatch = elem;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\telem = elem[dir];\n\t\t\t}\n\n\t\t\tcheckSet[i] = match;\n\t\t}\n\t}\n}\n\nvar contains = document.compareDocumentPosition ? function(a, b){\n\treturn !!(a.compareDocumentPosition(b) & 16);\n} : function(a, b){\n\treturn a !== b && (a.contains ? a.contains(b) : true);\n};\n\nvar isXML = function(elem){\n\t// documentElement is verified for cases where it doesn't yet exist\n\t// (such as loading iframes in IE - #4833) \n\tvar documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;\n\treturn documentElement ? documentElement.nodeName !== \"HTML\" : false;\n};\n\nvar posProcess = function(selector, context){\n\tvar tmpSet = [], later = \"\", match,\n\t\troot = context.nodeType ? [context] : context;\n\n\t// Position selectors must be done after the filter\n\t// And so must :not(positional) so we move all PSEUDOs to the end\n\twhile ( (match = Expr.match.PSEUDO.exec( selector )) ) {\n\t\tlater += match[0];\n\t\tselector = selector.replace( Expr.match.PSEUDO, \"\" );\n\t}\n\n\tselector = Expr.relative[selector] ? selector + \"*\" : selector;\n\n\tfor ( var i = 0, l = root.length; i < l; i++ ) {\n\t\tSizzle( selector, root[i], tmpSet );\n\t}\n\n\treturn Sizzle.filter( later, tmpSet );\n};\n\n// EXPOSE\njQuery.find = Sizzle;\njQuery.expr = Sizzle.selectors;\njQuery.expr[\":\"] = jQuery.expr.filters;\njQuery.unique = Sizzle.uniqueSort;\njQuery.text = getText;\njQuery.isXMLDoc = isXML;\njQuery.contains = contains;\n\nreturn;\n\nwindow.Sizzle = Sizzle;\n\n})();\nvar runtil = /Until$/,\n\trparentsprev = /^(?:parents|prevUntil|prevAll)/,\n\t// Note: This RegExp should be improved, or likely pulled from Sizzle\n\trmultiselector = /,/,\n\tslice = Array.prototype.slice;\n\n// Implement the identical functionality for filter and not\nvar winnow = function( elements, qualifier, keep ) {\n\tif ( jQuery.isFunction( qualifier ) ) {\n\t\treturn jQuery.grep(elements, function( elem, i ) {\n\t\t\treturn !!qualifier.call( elem, i, elem ) === keep;\n\t\t});\n\n\t} else if ( qualifier.nodeType ) {\n\t\treturn jQuery.grep(elements, function( elem, i ) {\n\t\t\treturn (elem === qualifier) === keep;\n\t\t});\n\n\t} else if ( typeof qualifier === \"string\" ) {\n\t\tvar filtered = jQuery.grep(elements, function( elem ) {\n\t\t\treturn elem.nodeType === 1;\n\t\t});\n\n\t\tif ( isSimple.test( qualifier ) ) {\n\t\t\treturn jQuery.filter(qualifier, filtered, !keep);\n\t\t} else {\n\t\t\tqualifier = jQuery.filter( qualifier, filtered );\n\t\t}\n\t}\n\n\treturn jQuery.grep(elements, function( elem, i ) {\n\t\treturn (jQuery.inArray( elem, qualifier ) >= 0) === keep;\n\t});\n};\n\njQuery.fn.extend({\n\tfind: function( selector ) {\n\t\tvar ret = this.pushStack( \"\", \"find\", selector ), length = 0;\n\n\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\tlength = ret.length;\n\t\t\tjQuery.find( selector, this[i], ret );\n\n\t\t\tif ( i > 0 ) {\n\t\t\t\t// Make sure that the results are unique\n\t\t\t\tfor ( var n = length; n < ret.length; n++ ) {\n\t\t\t\t\tfor ( var r = 0; r < length; r++ ) {\n\t\t\t\t\t\tif ( ret[r] === ret[n] ) {\n\t\t\t\t\t\t\tret.splice(n--, 1);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\thas: function( target ) {\n\t\tvar targets = jQuery( target );\n\t\treturn this.filter(function() {\n\t\t\tfor ( var i = 0, l = targets.length; i < l; i++ ) {\n\t\t\t\tif ( jQuery.contains( this, targets[i] ) ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t},\n\n\tnot: function( selector ) {\n\t\treturn this.pushStack( winnow(this, selector, false), \"not\", selector);\n\t},\n\n\tfilter: function( selector ) {\n\t\treturn this.pushStack( winnow(this, selector, true), \"filter\", selector );\n\t},\n\t\n\tis: function( selector ) {\n\t\treturn !!selector && jQuery.filter( selector, this ).length > 0;\n\t},\n\n\tclosest: function( selectors, context ) {\n\t\tif ( jQuery.isArray( selectors ) ) {\n\t\t\tvar ret = [], cur = this[0], match, matches = {}, selector;\n\n\t\t\tif ( cur && selectors.length ) {\n\t\t\t\tfor ( var i = 0, l = selectors.length; i < l; i++ ) {\n\t\t\t\t\tselector = selectors[i];\n\n\t\t\t\t\tif ( !matches[selector] ) {\n\t\t\t\t\t\tmatches[selector] = jQuery.expr.match.POS.test( selector ) ? \n\t\t\t\t\t\t\tjQuery( selector, context || this.context ) :\n\t\t\t\t\t\t\tselector;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\twhile ( cur && cur.ownerDocument && cur !== context ) {\n\t\t\t\t\tfor ( selector in matches ) {\n\t\t\t\t\t\tmatch = matches[selector];\n\n\t\t\t\t\t\tif ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {\n\t\t\t\t\t\t\tret.push({ selector: selector, elem: cur });\n\t\t\t\t\t\t\tdelete matches[selector];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcur = cur.parentNode;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tvar pos = jQuery.expr.match.POS.test( selectors ) ? \n\t\t\tjQuery( selectors, context || this.context ) : null;\n\n\t\treturn this.map(function( i, cur ) {\n\t\t\twhile ( cur && cur.ownerDocument && cur !== context ) {\n\t\t\t\tif ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {\n\t\t\t\t\treturn cur;\n\t\t\t\t}\n\t\t\t\tcur = cur.parentNode;\n\t\t\t}\n\t\t\treturn null;\n\t\t});\n\t},\n\t\n\t// Determine the position of an element within\n\t// the matched set of elements\n\tindex: function( elem ) {\n\t\tif ( !elem || typeof elem === \"string\" ) {\n\t\t\treturn jQuery.inArray( this[0],\n\t\t\t\t// If it receives a string, the selector is used\n\t\t\t\t// If it receives nothing, the siblings are used\n\t\t\t\telem ? jQuery( elem ) : this.parent().children() );\n\t\t}\n\t\t// Locate the position of the desired element\n\t\treturn jQuery.inArray(\n\t\t\t// If it receives a jQuery object, the first element is used\n\t\t\telem.jquery ? elem[0] : elem, this );\n\t},\n\n\tadd: function( selector, context ) {\n\t\tvar set = typeof selector === \"string\" ?\n\t\t\t\tjQuery( selector, context || this.context ) :\n\t\t\t\tjQuery.makeArray( selector ),\n\t\t\tall = jQuery.merge( this.get(), set );\n\n\t\treturn this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?\n\t\t\tall :\n\t\t\tjQuery.unique( all ) );\n\t},\n\n\tandSelf: function() {\n\t\treturn this.add( this.prevObject );\n\t}\n});\n\n// A painfully simple check to see if an element is disconnected\n// from a document (should be improved, where feasible).\nfunction isDisconnected( node ) {\n\treturn !node || !node.parentNode || node.parentNode.nodeType === 11;\n}\n\njQuery.each({\n\tparent: function( elem ) {\n\t\tvar parent = elem.parentNode;\n\t\treturn parent && parent.nodeType !== 11 ? parent : null;\n\t},\n\tparents: function( elem ) {\n\t\treturn jQuery.dir( elem, \"parentNode\" );\n\t},\n\tparentsUntil: function( elem, i, until ) {\n\t\treturn jQuery.dir( elem, \"parentNode\", until );\n\t},\n\tnext: function( elem ) {\n\t\treturn jQuery.nth( elem, 2, \"nextSibling\" );\n\t},\n\tprev: function( elem ) {\n\t\treturn jQuery.nth( elem, 2, \"previousSibling\" );\n\t},\n\tnextAll: function( elem ) {\n\t\treturn jQuery.dir( elem, \"nextSibling\" );\n\t},\n\tprevAll: function( elem ) {\n\t\treturn jQuery.dir( elem, \"previousSibling\" );\n\t},\n\tnextUntil: function( elem, i, until ) {\n\t\treturn jQuery.dir( elem, \"nextSibling\", until );\n\t},\n\tprevUntil: function( elem, i, until ) {\n\t\treturn jQuery.dir( elem, \"previousSibling\", until );\n\t},\n\tsiblings: function( elem ) {\n\t\treturn jQuery.sibling( elem.parentNode.firstChild, elem );\n\t},\n\tchildren: function( elem ) {\n\t\treturn jQuery.sibling( elem.firstChild );\n\t},\n\tcontents: function( elem ) {\n\t\treturn jQuery.nodeName( elem, \"iframe\" ) ?\n\t\t\telem.contentDocument || elem.contentWindow.document :\n\t\t\tjQuery.makeArray( elem.childNodes );\n\t}\n}, function( name, fn ) {\n\tjQuery.fn[ name ] = function( until, selector ) {\n\t\tvar ret = jQuery.map( this, fn, until );\n\t\t\n\t\tif ( !runtil.test( name ) ) {\n\t\t\tselector = until;\n\t\t}\n\n\t\tif ( selector && typeof selector === \"string\" ) {\n\t\t\tret = jQuery.filter( selector, ret );\n\t\t}\n\n\t\tret = this.length > 1 ? jQuery.unique( ret ) : ret;\n\n\t\tif ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {\n\t\t\tret = ret.reverse();\n\t\t}\n\n\t\treturn this.pushStack( ret, name, slice.call(arguments).join(\",\") );\n\t};\n});\n\njQuery.extend({\n\tfilter: function( expr, elems, not ) {\n\t\tif ( not ) {\n\t\t\texpr = \":not(\" + expr + \")\";\n\t\t}\n\n\t\treturn jQuery.find.matches(expr, elems);\n\t},\n\t\n\tdir: function( elem, dir, until ) {\n\t\tvar matched = [], cur = elem[dir];\n\t\twhile ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {\n\t\t\tif ( cur.nodeType === 1 ) {\n\t\t\t\tmatched.push( cur );\n\t\t\t}\n\t\t\tcur = cur[dir];\n\t\t}\n\t\treturn matched;\n\t},\n\n\tnth: function( cur, result, dir, elem ) {\n\t\tresult = result || 1;\n\t\tvar num = 0;\n\n\t\tfor ( ; cur; cur = cur[dir] ) {\n\t\t\tif ( cur.nodeType === 1 && ++num === result ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn cur;\n\t},\n\n\tsibling: function( n, elem ) {\n\t\tvar r = [];\n\n\t\tfor ( ; n; n = n.nextSibling ) {\n\t\t\tif ( n.nodeType === 1 && n !== elem ) {\n\t\t\t\tr.push( n );\n\t\t\t}\n\t\t}\n\n\t\treturn r;\n\t}\n});\nvar rinlinejQuery = / jQuery\\d+=\"(?:\\d+|null)\"/g,\n\trleadingWhitespace = /^\\s+/,\n\trxhtmlTag = /(<([\\w:]+)[^>]*?)\\/>/g,\n\trselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,\n\trtagName = /<([\\w:]+)/,\n\trtbody = /<tbody/i,\n\trhtml = /<|&#?\\w+;/,\n\trnocache = /<script|<object|<embed|<option|<style/i,\n\trchecked = /checked\\s*(?:[^=]|=\\s*.checked.)/i,  // checked=\"checked\" or checked (html5)\n\tfcloseTag = function( all, front, tag ) {\n\t\treturn rselfClosing.test( tag ) ?\n\t\t\tall :\n\t\t\tfront + \"></\" + tag + \">\";\n\t},\n\twrapMap = {\n\t\toption: [ 1, \"<select multiple='multiple'>\", \"</select>\" ],\n\t\tlegend: [ 1, \"<fieldset>\", \"</fieldset>\" ],\n\t\tthead: [ 1, \"<table>\", \"</table>\" ],\n\t\ttr: [ 2, \"<table><tbody>\", \"</tbody></table>\" ],\n\t\ttd: [ 3, \"<table><tbody><tr>\", \"</tr></tbody></table>\" ],\n\t\tcol: [ 2, \"<table><tbody></tbody><colgroup>\", \"</colgroup></table>\" ],\n\t\tarea: [ 1, \"<map>\", \"</map>\" ],\n\t\t_default: [ 0, \"\", \"\" ]\n\t};\n\nwrapMap.optgroup = wrapMap.option;\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n// IE can't serialize <link> and <script> tags normally\nif ( !jQuery.support.htmlSerialize ) {\n\twrapMap._default = [ 1, \"div<div>\", \"</div>\" ];\n}\n\njQuery.fn.extend({\n\ttext: function( text ) {\n\t\tif ( jQuery.isFunction(text) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery(this);\n\t\t\t\tself.text( text.call(this, i, self.text()) );\n\t\t\t});\n\t\t}\n\n\t\tif ( typeof text !== \"object\" && text !== undefined ) {\n\t\t\treturn this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );\n\t\t}\n\n\t\treturn jQuery.text( this );\n\t},\n\n\twrapAll: function( html ) {\n\t\tif ( jQuery.isFunction( html ) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tjQuery(this).wrapAll( html.call(this, i) );\n\t\t\t});\n\t\t}\n\n\t\tif ( this[0] ) {\n\t\t\t// The elements to wrap the target around\n\t\t\tvar wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);\n\n\t\t\tif ( this[0].parentNode ) {\n\t\t\t\twrap.insertBefore( this[0] );\n\t\t\t}\n\n\t\t\twrap.map(function() {\n\t\t\t\tvar elem = this;\n\n\t\t\t\twhile ( elem.firstChild && elem.firstChild.nodeType === 1 ) {\n\t\t\t\t\telem = elem.firstChild;\n\t\t\t\t}\n\n\t\t\t\treturn elem;\n\t\t\t}).append(this);\n\t\t}\n\n\t\treturn this;\n\t},\n\n\twrapInner: function( html ) {\n\t\tif ( jQuery.isFunction( html ) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tjQuery(this).wrapInner( html.call(this, i) );\n\t\t\t});\n\t\t}\n\n\t\treturn this.each(function() {\n\t\t\tvar self = jQuery( this ), contents = self.contents();\n\n\t\t\tif ( contents.length ) {\n\t\t\t\tcontents.wrapAll( html );\n\n\t\t\t} else {\n\t\t\t\tself.append( html );\n\t\t\t}\n\t\t});\n\t},\n\n\twrap: function( html ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery( this ).wrapAll( html );\n\t\t});\n\t},\n\n\tunwrap: function() {\n\t\treturn this.parent().each(function() {\n\t\t\tif ( !jQuery.nodeName( this, \"body\" ) ) {\n\t\t\t\tjQuery( this ).replaceWith( this.childNodes );\n\t\t\t}\n\t\t}).end();\n\t},\n\n\tappend: function() {\n\t\treturn this.domManip(arguments, true, function( elem ) {\n\t\t\tif ( this.nodeType === 1 ) {\n\t\t\t\tthis.appendChild( elem );\n\t\t\t}\n\t\t});\n\t},\n\n\tprepend: function() {\n\t\treturn this.domManip(arguments, true, function( elem ) {\n\t\t\tif ( this.nodeType === 1 ) {\n\t\t\t\tthis.insertBefore( elem, this.firstChild );\n\t\t\t}\n\t\t});\n\t},\n\n\tbefore: function() {\n\t\tif ( this[0] && this[0].parentNode ) {\n\t\t\treturn this.domManip(arguments, false, function( elem ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this );\n\t\t\t});\n\t\t} else if ( arguments.length ) {\n\t\t\tvar set = jQuery(arguments[0]);\n\t\t\tset.push.apply( set, this.toArray() );\n\t\t\treturn this.pushStack( set, \"before\", arguments );\n\t\t}\n\t},\n\n\tafter: function() {\n\t\tif ( this[0] && this[0].parentNode ) {\n\t\t\treturn this.domManip(arguments, false, function( elem ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this.nextSibling );\n\t\t\t});\n\t\t} else if ( arguments.length ) {\n\t\t\tvar set = this.pushStack( this, \"after\", arguments );\n\t\t\tset.push.apply( set, jQuery(arguments[0]).toArray() );\n\t\t\treturn set;\n\t\t}\n\t},\n\t\n\t// keepData is for internal use only--do not document\n\tremove: function( selector, keepData ) {\n\t\tfor ( var i = 0, elem; (elem = this[i]) != null; i++ ) {\n\t\t\tif ( !selector || jQuery.filter( selector, [ elem ] ).length ) {\n\t\t\t\tif ( !keepData && elem.nodeType === 1 ) {\n\t\t\t\t\tjQuery.cleanData( elem.getElementsByTagName(\"*\") );\n\t\t\t\t\tjQuery.cleanData( [ elem ] );\n\t\t\t\t}\n\n\t\t\t\tif ( elem.parentNode ) {\n\t\t\t\t\t elem.parentNode.removeChild( elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn this;\n\t},\n\n\tempty: function() {\n\t\tfor ( var i = 0, elem; (elem = this[i]) != null; i++ ) {\n\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\tjQuery.cleanData( elem.getElementsByTagName(\"*\") );\n\t\t\t}\n\n\t\t\t// Remove any remaining nodes\n\t\t\twhile ( elem.firstChild ) {\n\t\t\t\telem.removeChild( elem.firstChild );\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn this;\n\t},\n\n\tclone: function( events ) {\n\t\t// Do the clone\n\t\tvar ret = this.map(function() {\n\t\t\tif ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {\n\t\t\t\t// IE copies events bound via attachEvent when\n\t\t\t\t// using cloneNode. Calling detachEvent on the\n\t\t\t\t// clone will also remove the events from the orignal\n\t\t\t\t// In order to get around this, we use innerHTML.\n\t\t\t\t// Unfortunately, this means some modifications to\n\t\t\t\t// attributes in IE that are actually only stored\n\t\t\t\t// as properties will not be copied (such as the\n\t\t\t\t// the name attribute on an input).\n\t\t\t\tvar html = this.outerHTML, ownerDocument = this.ownerDocument;\n\t\t\t\tif ( !html ) {\n\t\t\t\t\tvar div = ownerDocument.createElement(\"div\");\n\t\t\t\t\tdiv.appendChild( this.cloneNode(true) );\n\t\t\t\t\thtml = div.innerHTML;\n\t\t\t\t}\n\n\t\t\t\treturn jQuery.clean([html.replace(rinlinejQuery, \"\")\n\t\t\t\t\t// Handle the case in IE 8 where action=/test/> self-closes a tag\n\t\t\t\t\t.replace(/=([^=\"'>\\s]+\\/)>/g, '=\"$1\">')\n\t\t\t\t\t.replace(rleadingWhitespace, \"\")], ownerDocument)[0];\n\t\t\t} else {\n\t\t\t\treturn this.cloneNode(true);\n\t\t\t}\n\t\t});\n\n\t\t// Copy the events from the original to the clone\n\t\tif ( events === true ) {\n\t\t\tcloneCopyEvent( this, ret );\n\t\t\tcloneCopyEvent( this.find(\"*\"), ret.find(\"*\") );\n\t\t}\n\n\t\t// Return the cloned set\n\t\treturn ret;\n\t},\n\n\thtml: function( value ) {\n\t\tif ( value === undefined ) {\n\t\t\treturn this[0] && this[0].nodeType === 1 ?\n\t\t\t\tthis[0].innerHTML.replace(rinlinejQuery, \"\") :\n\t\t\t\tnull;\n\n\t\t// See if we can take a shortcut and just use innerHTML\n\t\t} else if ( typeof value === \"string\" && !rnocache.test( value ) &&\n\t\t\t(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&\n\t\t\t!wrapMap[ (rtagName.exec( value ) || [\"\", \"\"])[1].toLowerCase() ] ) {\n\n\t\t\tvalue = value.replace(rxhtmlTag, fcloseTag);\n\n\t\t\ttry {\n\t\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\t\t\tif ( this[i].nodeType === 1 ) {\n\t\t\t\t\t\tjQuery.cleanData( this[i].getElementsByTagName(\"*\") );\n\t\t\t\t\t\tthis[i].innerHTML = value;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t// If using innerHTML throws an exception, use the fallback method\n\t\t\t} catch(e) {\n\t\t\t\tthis.empty().append( value );\n\t\t\t}\n\n\t\t} else if ( jQuery.isFunction( value ) ) {\n\t\t\tthis.each(function(i){\n\t\t\t\tvar self = jQuery(this), old = self.html();\n\t\t\t\tself.empty().append(function(){\n\t\t\t\t\treturn value.call( this, i, old );\n\t\t\t\t});\n\t\t\t});\n\n\t\t} else {\n\t\t\tthis.empty().append( value );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\treplaceWith: function( value ) {\n\t\tif ( this[0] && this[0].parentNode ) {\n\t\t\t// Make sure that the elements are removed from the DOM before they are inserted\n\t\t\t// this can help fix replacing a parent with child elements\n\t\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\t\treturn this.each(function(i) {\n\t\t\t\t\tvar self = jQuery(this), old = self.html();\n\t\t\t\t\tself.replaceWith( value.call( this, i, old ) );\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif ( typeof value !== \"string\" ) {\n\t\t\t\tvalue = jQuery(value).detach();\n\t\t\t}\n\n\t\t\treturn this.each(function() {\n\t\t\t\tvar next = this.nextSibling, parent = this.parentNode;\n\n\t\t\t\tjQuery(this).remove();\n\n\t\t\t\tif ( next ) {\n\t\t\t\t\tjQuery(next).before( value );\n\t\t\t\t} else {\n\t\t\t\t\tjQuery(parent).append( value );\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\treturn this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), \"replaceWith\", value );\n\t\t}\n\t},\n\n\tdetach: function( selector ) {\n\t\treturn this.remove( selector, true );\n\t},\n\n\tdomManip: function( args, table, callback ) {\n\t\tvar results, first, value = args[0], scripts = [], fragment, parent;\n\n\t\t// We can't cloneNode fragments that contain checked, in WebKit\n\t\tif ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === \"string\" && rchecked.test( value ) ) {\n\t\t\treturn this.each(function() {\n\t\t\t\tjQuery(this).domManip( args, table, callback, true );\n\t\t\t});\n\t\t}\n\n\t\tif ( jQuery.isFunction(value) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery(this);\n\t\t\t\targs[0] = value.call(this, i, table ? self.html() : undefined);\n\t\t\t\tself.domManip( args, table, callback );\n\t\t\t});\n\t\t}\n\n\t\tif ( this[0] ) {\n\t\t\tparent = value && value.parentNode;\n\n\t\t\t// If we're in a fragment, just use that instead of building a new one\n\t\t\tif ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {\n\t\t\t\tresults = { fragment: parent };\n\n\t\t\t} else {\n\t\t\t\tresults = buildFragment( args, this, scripts );\n\t\t\t}\n\t\t\t\n\t\t\tfragment = results.fragment;\n\t\t\t\n\t\t\tif ( fragment.childNodes.length === 1 ) {\n\t\t\t\tfirst = fragment = fragment.firstChild;\n\t\t\t} else {\n\t\t\t\tfirst = fragment.firstChild;\n\t\t\t}\n\n\t\t\tif ( first ) {\n\t\t\t\ttable = table && jQuery.nodeName( first, \"tr\" );\n\n\t\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\t\tcallback.call(\n\t\t\t\t\t\ttable ?\n\t\t\t\t\t\t\troot(this[i], first) :\n\t\t\t\t\t\t\tthis[i],\n\t\t\t\t\t\ti > 0 || results.cacheable || this.length > 1  ?\n\t\t\t\t\t\t\tfragment.cloneNode(true) :\n\t\t\t\t\t\t\tfragment\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( scripts.length ) {\n\t\t\t\tjQuery.each( scripts, evalScript );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\n\t\tfunction root( elem, cur ) {\n\t\t\treturn jQuery.nodeName(elem, \"table\") ?\n\t\t\t\t(elem.getElementsByTagName(\"tbody\")[0] ||\n\t\t\t\telem.appendChild(elem.ownerDocument.createElement(\"tbody\"))) :\n\t\t\t\telem;\n\t\t}\n\t}\n});\n\nfunction cloneCopyEvent(orig, ret) {\n\tvar i = 0;\n\n\tret.each(function() {\n\t\tif ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;\n\n\t\tif ( events ) {\n\t\t\tdelete curData.handle;\n\t\t\tcurData.events = {};\n\n\t\t\tfor ( var type in events ) {\n\t\t\t\tfor ( var handler in events[ type ] ) {\n\t\t\t\t\tjQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n}\n\nfunction buildFragment( args, nodes, scripts ) {\n\tvar fragment, cacheable, cacheresults,\n\t\tdoc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);\n\n\t// Only cache \"small\" (1/2 KB) strings that are associated with the main document\n\t// Cloning options loses the selected state, so don't cache them\n\t// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment\n\t// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache\n\tif ( args.length === 1 && typeof args[0] === \"string\" && args[0].length < 512 && doc === document &&\n\t\t!rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {\n\n\t\tcacheable = true;\n\t\tcacheresults = jQuery.fragments[ args[0] ];\n\t\tif ( cacheresults ) {\n\t\t\tif ( cacheresults !== 1 ) {\n\t\t\t\tfragment = cacheresults;\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( !fragment ) {\n\t\tfragment = doc.createDocumentFragment();\n\t\tjQuery.clean( args, doc, fragment, scripts );\n\t}\n\n\tif ( cacheable ) {\n\t\tjQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;\n\t}\n\n\treturn { fragment: fragment, cacheable: cacheable };\n}\n\njQuery.fragments = {};\n\njQuery.each({\n\tappendTo: \"append\",\n\tprependTo: \"prepend\",\n\tinsertBefore: \"before\",\n\tinsertAfter: \"after\",\n\treplaceAll: \"replaceWith\"\n}, function( name, original ) {\n\tjQuery.fn[ name ] = function( selector ) {\n\t\tvar ret = [], insert = jQuery( selector ),\n\t\t\tparent = this.length === 1 && this[0].parentNode;\n\t\t\n\t\tif ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {\n\t\t\tinsert[ original ]( this[0] );\n\t\t\treturn this;\n\t\t\t\n\t\t} else {\n\t\t\tfor ( var i = 0, l = insert.length; i < l; i++ ) {\n\t\t\t\tvar elems = (i > 0 ? this.clone(true) : this).get();\n\t\t\t\tjQuery.fn[ original ].apply( jQuery(insert[i]), elems );\n\t\t\t\tret = ret.concat( elems );\n\t\t\t}\n\t\t\n\t\t\treturn this.pushStack( ret, name, insert.selector );\n\t\t}\n\t};\n});\n\njQuery.extend({\n\tclean: function( elems, context, fragment, scripts ) {\n\t\tcontext = context || document;\n\n\t\t// !context.createElement fails in IE with an error but returns typeof 'object'\n\t\tif ( typeof context.createElement === \"undefined\" ) {\n\t\t\tcontext = context.ownerDocument || context[0] && context[0].ownerDocument || document;\n\t\t}\n\n\t\tvar ret = [];\n\n\t\tfor ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {\n\t\t\tif ( typeof elem === \"number\" ) {\n\t\t\t\telem += \"\";\n\t\t\t}\n\n\t\t\tif ( !elem ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Convert html string into DOM nodes\n\t\t\tif ( typeof elem === \"string\" && !rhtml.test( elem ) ) {\n\t\t\t\telem = context.createTextNode( elem );\n\n\t\t\t} else if ( typeof elem === \"string\" ) {\n\t\t\t\t// Fix \"XHTML\"-style tags in all browsers\n\t\t\t\telem = elem.replace(rxhtmlTag, fcloseTag);\n\n\t\t\t\t// Trim whitespace, otherwise indexOf won't work as expected\n\t\t\t\tvar tag = (rtagName.exec( elem ) || [\"\", \"\"])[1].toLowerCase(),\n\t\t\t\t\twrap = wrapMap[ tag ] || wrapMap._default,\n\t\t\t\t\tdepth = wrap[0],\n\t\t\t\t\tdiv = context.createElement(\"div\");\n\n\t\t\t\t// Go to html and back, then peel off extra wrappers\n\t\t\t\tdiv.innerHTML = wrap[1] + elem + wrap[2];\n\n\t\t\t\t// Move to the right depth\n\t\t\t\twhile ( depth-- ) {\n\t\t\t\t\tdiv = div.lastChild;\n\t\t\t\t}\n\n\t\t\t\t// Remove IE's autoinserted <tbody> from table fragments\n\t\t\t\tif ( !jQuery.support.tbody ) {\n\n\t\t\t\t\t// String was a <table>, *may* have spurious <tbody>\n\t\t\t\t\tvar hasBody = rtbody.test(elem),\n\t\t\t\t\t\ttbody = tag === \"table\" && !hasBody ?\n\t\t\t\t\t\t\tdiv.firstChild && div.firstChild.childNodes :\n\n\t\t\t\t\t\t\t// String was a bare <thead> or <tfoot>\n\t\t\t\t\t\t\twrap[1] === \"<table>\" && !hasBody ?\n\t\t\t\t\t\t\t\tdiv.childNodes :\n\t\t\t\t\t\t\t\t[];\n\n\t\t\t\t\tfor ( var j = tbody.length - 1; j >= 0 ; --j ) {\n\t\t\t\t\t\tif ( jQuery.nodeName( tbody[ j ], \"tbody\" ) && !tbody[ j ].childNodes.length ) {\n\t\t\t\t\t\t\ttbody[ j ].parentNode.removeChild( tbody[ j ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// IE completely kills leading whitespace when innerHTML is used\n\t\t\t\tif ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {\n\t\t\t\t\tdiv.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );\n\t\t\t\t}\n\n\t\t\t\telem = div.childNodes;\n\t\t\t}\n\n\t\t\tif ( elem.nodeType ) {\n\t\t\t\tret.push( elem );\n\t\t\t} else {\n\t\t\t\tret = jQuery.merge( ret, elem );\n\t\t\t}\n\t\t}\n\n\t\tif ( fragment ) {\n\t\t\tfor ( var i = 0; ret[i]; i++ ) {\n\t\t\t\tif ( scripts && jQuery.nodeName( ret[i], \"script\" ) && (!ret[i].type || ret[i].type.toLowerCase() === \"text/javascript\") ) {\n\t\t\t\t\tscripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );\n\t\t\t\t\n\t\t\t\t} else {\n\t\t\t\t\tif ( ret[i].nodeType === 1 ) {\n\t\t\t\t\t\tret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName(\"script\"))) );\n\t\t\t\t\t}\n\t\t\t\t\tfragment.appendChild( ret[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\t\n\tcleanData: function( elems ) {\n\t\tvar data, id, cache = jQuery.cache,\n\t\t\tspecial = jQuery.event.special,\n\t\t\tdeleteExpando = jQuery.support.deleteExpando;\n\t\t\n\t\tfor ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {\n\t\t\tid = elem[ jQuery.expando ];\n\t\t\t\n\t\t\tif ( id ) {\n\t\t\t\tdata = cache[ id ];\n\t\t\t\t\n\t\t\t\tif ( data.events ) {\n\t\t\t\t\tfor ( var type in data.events ) {\n\t\t\t\t\t\tif ( special[ type ] ) {\n\t\t\t\t\t\t\tjQuery.event.remove( elem, type );\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tremoveEvent( elem, type, data.handle );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif ( deleteExpando ) {\n\t\t\t\t\tdelete elem[ jQuery.expando ];\n\n\t\t\t\t} else if ( elem.removeAttribute ) {\n\t\t\t\t\telem.removeAttribute( jQuery.expando );\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tdelete cache[ id ];\n\t\t\t}\n\t\t}\n\t}\n});\n// exclude the following css properties to add px\nvar rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,\n\tralpha = /alpha\\([^)]*\\)/,\n\tropacity = /opacity=([^)]*)/,\n\trfloat = /float/i,\n\trdashAlpha = /-([a-z])/ig,\n\trupper = /([A-Z])/g,\n\trnumpx = /^-?\\d+(?:px)?$/i,\n\trnum = /^-?\\d/,\n\n\tcssShow = { position: \"absolute\", visibility: \"hidden\", display:\"block\" },\n\tcssWidth = [ \"Left\", \"Right\" ],\n\tcssHeight = [ \"Top\", \"Bottom\" ],\n\n\t// cache check for defaultView.getComputedStyle\n\tgetComputedStyle = document.defaultView && document.defaultView.getComputedStyle,\n\t// normalize float css property\n\tstyleFloat = jQuery.support.cssFloat ? \"cssFloat\" : \"styleFloat\",\n\tfcamelCase = function( all, letter ) {\n\t\treturn letter.toUpperCase();\n\t};\n\njQuery.fn.css = function( name, value ) {\n\treturn access( this, name, value, true, function( elem, name, value ) {\n\t\tif ( value === undefined ) {\n\t\t\treturn jQuery.curCSS( elem, name );\n\t\t}\n\t\t\n\t\tif ( typeof value === \"number\" && !rexclude.test(name) ) {\n\t\t\tvalue += \"px\";\n\t\t}\n\n\t\tjQuery.style( elem, name, value );\n\t});\n};\n\njQuery.extend({\n\tstyle: function( elem, name, value ) {\n\t\t// don't set styles on text and comment nodes\n\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// ignore negative width and height values #1599\n\t\tif ( (name === \"width\" || name === \"height\") && parseFloat(value) < 0 ) {\n\t\t\tvalue = undefined;\n\t\t}\n\n\t\tvar style = elem.style || elem, set = value !== undefined;\n\n\t\t// IE uses filters for opacity\n\t\tif ( !jQuery.support.opacity && name === \"opacity\" ) {\n\t\t\tif ( set ) {\n\t\t\t\t// IE has trouble with opacity if it does not have layout\n\t\t\t\t// Force it by setting the zoom level\n\t\t\t\tstyle.zoom = 1;\n\n\t\t\t\t// Set the alpha filter to set the opacity\n\t\t\t\tvar opacity = parseInt( value, 10 ) + \"\" === \"NaN\" ? \"\" : \"alpha(opacity=\" + value * 100 + \")\";\n\t\t\t\tvar filter = style.filter || jQuery.curCSS( elem, \"filter\" ) || \"\";\n\t\t\t\tstyle.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;\n\t\t\t}\n\n\t\t\treturn style.filter && style.filter.indexOf(\"opacity=\") >= 0 ?\n\t\t\t\t(parseFloat( ropacity.exec(style.filter)[1] ) / 100) + \"\":\n\t\t\t\t\"\";\n\t\t}\n\n\t\t// Make sure we're using the right name for getting the float value\n\t\tif ( rfloat.test( name ) ) {\n\t\t\tname = styleFloat;\n\t\t}\n\n\t\tname = name.replace(rdashAlpha, fcamelCase);\n\n\t\tif ( set ) {\n\t\t\tstyle[ name ] = value;\n\t\t}\n\n\t\treturn style[ name ];\n\t},\n\n\tcss: function( elem, name, force, extra ) {\n\t\tif ( name === \"width\" || name === \"height\" ) {\n\t\t\tvar val, props = cssShow, which = name === \"width\" ? cssWidth : cssHeight;\n\n\t\t\tfunction getWH() {\n\t\t\t\tval = name === \"width\" ? elem.offsetWidth : elem.offsetHeight;\n\n\t\t\t\tif ( extra === \"border\" ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tjQuery.each( which, function() {\n\t\t\t\t\tif ( !extra ) {\n\t\t\t\t\t\tval -= parseFloat(jQuery.curCSS( elem, \"padding\" + this, true)) || 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( extra === \"margin\" ) {\n\t\t\t\t\t\tval += parseFloat(jQuery.curCSS( elem, \"margin\" + this, true)) || 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tval -= parseFloat(jQuery.curCSS( elem, \"border\" + this + \"Width\", true)) || 0;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif ( elem.offsetWidth !== 0 ) {\n\t\t\t\tgetWH();\n\t\t\t} else {\n\t\t\t\tjQuery.swap( elem, props, getWH );\n\t\t\t}\n\n\t\t\treturn Math.max(0, Math.round(val));\n\t\t}\n\n\t\treturn jQuery.curCSS( elem, name, force );\n\t},\n\n\tcurCSS: function( elem, name, force ) {\n\t\tvar ret, style = elem.style, filter;\n\n\t\t// IE uses filters for opacity\n\t\tif ( !jQuery.support.opacity && name === \"opacity\" && elem.currentStyle ) {\n\t\t\tret = ropacity.test(elem.currentStyle.filter || \"\") ?\n\t\t\t\t(parseFloat(RegExp.$1) / 100) + \"\" :\n\t\t\t\t\"\";\n\n\t\t\treturn ret === \"\" ?\n\t\t\t\t\"1\" :\n\t\t\t\tret;\n\t\t}\n\n\t\t// Make sure we're using the right name for getting the float value\n\t\tif ( rfloat.test( name ) ) {\n\t\t\tname = styleFloat;\n\t\t}\n\n\t\tif ( !force && style && style[ name ] ) {\n\t\t\tret = style[ name ];\n\n\t\t} else if ( getComputedStyle ) {\n\n\t\t\t// Only \"float\" is needed here\n\t\t\tif ( rfloat.test( name ) ) {\n\t\t\t\tname = \"float\";\n\t\t\t}\n\n\t\t\tname = name.replace( rupper, \"-$1\" ).toLowerCase();\n\n\t\t\tvar defaultView = elem.ownerDocument.defaultView;\n\n\t\t\tif ( !defaultView ) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tvar computedStyle = defaultView.getComputedStyle( elem, null );\n\n\t\t\tif ( computedStyle ) {\n\t\t\t\tret = computedStyle.getPropertyValue( name );\n\t\t\t}\n\n\t\t\t// We should always get a number back from opacity\n\t\t\tif ( name === \"opacity\" && ret === \"\" ) {\n\t\t\t\tret = \"1\";\n\t\t\t}\n\n\t\t} else if ( elem.currentStyle ) {\n\t\t\tvar camelCase = name.replace(rdashAlpha, fcamelCase);\n\n\t\t\tret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];\n\n\t\t\t// From the awesome hack by Dean Edwards\n\t\t\t// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291\n\n\t\t\t// If we're not dealing with a regular pixel number\n\t\t\t// but a number that has a weird ending, we need to convert it to pixels\n\t\t\tif ( !rnumpx.test( ret ) && rnum.test( ret ) ) {\n\t\t\t\t// Remember the original values\n\t\t\t\tvar left = style.left, rsLeft = elem.runtimeStyle.left;\n\n\t\t\t\t// Put in the new values to get a computed value out\n\t\t\t\telem.runtimeStyle.left = elem.currentStyle.left;\n\t\t\t\tstyle.left = camelCase === \"fontSize\" ? \"1em\" : (ret || 0);\n\t\t\t\tret = style.pixelLeft + \"px\";\n\n\t\t\t\t// Revert the changed values\n\t\t\t\tstyle.left = left;\n\t\t\t\telem.runtimeStyle.left = rsLeft;\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\t// A method for quickly swapping in/out CSS properties to get correct calculations\n\tswap: function( elem, options, callback ) {\n\t\tvar old = {};\n\n\t\t// Remember the old values, and insert the new ones\n\t\tfor ( var name in options ) {\n\t\t\told[ name ] = elem.style[ name ];\n\t\t\telem.style[ name ] = options[ name ];\n\t\t}\n\n\t\tcallback.call( elem );\n\n\t\t// Revert the old values\n\t\tfor ( var name in options ) {\n\t\t\telem.style[ name ] = old[ name ];\n\t\t}\n\t}\n});\n\nif ( jQuery.expr && jQuery.expr.filters ) {\n\tjQuery.expr.filters.hidden = function( elem ) {\n\t\tvar width = elem.offsetWidth, height = elem.offsetHeight,\n\t\t\tskip = elem.nodeName.toLowerCase() === \"tr\";\n\n\t\treturn width === 0 && height === 0 && !skip ?\n\t\t\ttrue :\n\t\t\twidth > 0 && height > 0 && !skip ?\n\t\t\t\tfalse :\n\t\t\t\tjQuery.curCSS(elem, \"display\") === \"none\";\n\t};\n\n\tjQuery.expr.filters.visible = function( elem ) {\n\t\treturn !jQuery.expr.filters.hidden( elem );\n\t};\n}\nvar jsc = now(),\n\trscript = /<script(.|\\s)*?\\/script>/gi,\n\trselectTextarea = /select|textarea/i,\n\trinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,\n\tjsre = /=\\?(&|$)/,\n\trquery = /\\?/,\n\trts = /(\\?|&)_=.*?(&|$)/,\n\trurl = /^(\\w+:)?\\/\\/([^\\/?#]+)/,\n\tr20 = /%20/g,\n\n\t// Keep a copy of the old load method\n\t_load = jQuery.fn.load;\n\njQuery.fn.extend({\n\tload: function( url, params, callback ) {\n\t\tif ( typeof url !== \"string\" ) {\n\t\t\treturn _load.call( this, url );\n\n\t\t// Don't do a request if no elements are being requested\n\t\t} else if ( !this.length ) {\n\t\t\treturn this;\n\t\t}\n\n\t\tvar off = url.indexOf(\" \");\n\t\tif ( off >= 0 ) {\n\t\t\tvar selector = url.slice(off, url.length);\n\t\t\turl = url.slice(0, off);\n\t\t}\n\n\t\t// Default to a GET request\n\t\tvar type = \"GET\";\n\n\t\t// If the second parameter was provided\n\t\tif ( params ) {\n\t\t\t// If it's a function\n\t\t\tif ( jQuery.isFunction( params ) ) {\n\t\t\t\t// We assume that it's the callback\n\t\t\t\tcallback = params;\n\t\t\t\tparams = null;\n\n\t\t\t// Otherwise, build a param string\n\t\t\t} else if ( typeof params === \"object\" ) {\n\t\t\t\tparams = jQuery.param( params, jQuery.ajaxSettings.traditional );\n\t\t\t\ttype = \"POST\";\n\t\t\t}\n\t\t}\n\n\t\tvar self = this;\n\n\t\t// Request the remote document\n\t\tjQuery.ajax({\n\t\t\turl: url,\n\t\t\ttype: type,\n\t\t\tdataType: \"html\",\n\t\t\tdata: params,\n\t\t\tcomplete: function( res, status ) {\n\t\t\t\t// If successful, inject the HTML into all the matched elements\n\t\t\t\tif ( status === \"success\" || status === \"notmodified\" ) {\n\t\t\t\t\t// See if a selector was specified\n\t\t\t\t\tself.html( selector ?\n\t\t\t\t\t\t// Create a dummy div to hold the results\n\t\t\t\t\t\tjQuery(\"<div />\")\n\t\t\t\t\t\t\t// inject the contents of the document in, removing the scripts\n\t\t\t\t\t\t\t// to avoid any 'Permission Denied' errors in IE\n\t\t\t\t\t\t\t.append(res.responseText.replace(rscript, \"\"))\n\n\t\t\t\t\t\t\t// Locate the specified elements\n\t\t\t\t\t\t\t.find(selector) :\n\n\t\t\t\t\t\t// If not, just inject the full result\n\t\t\t\t\t\tres.responseText );\n\t\t\t\t}\n\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tself.each( callback, [res.responseText, status, res] );\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\treturn this;\n\t},\n\n\tserialize: function() {\n\t\treturn jQuery.param(this.serializeArray());\n\t},\n\tserializeArray: function() {\n\t\treturn this.map(function() {\n\t\t\treturn this.elements ? jQuery.makeArray(this.elements) : this;\n\t\t})\n\t\t.filter(function() {\n\t\t\treturn this.name && !this.disabled &&\n\t\t\t\t(this.checked || rselectTextarea.test(this.nodeName) ||\n\t\t\t\t\trinput.test(this.type));\n\t\t})\n\t\t.map(function( i, elem ) {\n\t\t\tvar val = jQuery(this).val();\n\n\t\t\treturn val == null ?\n\t\t\t\tnull :\n\t\t\t\tjQuery.isArray(val) ?\n\t\t\t\t\tjQuery.map( val, function( val, i ) {\n\t\t\t\t\t\treturn { name: elem.name, value: val };\n\t\t\t\t\t}) :\n\t\t\t\t\t{ name: elem.name, value: val };\n\t\t}).get();\n\t}\n});\n\n// Attach a bunch of functions for handling common AJAX events\njQuery.each( \"ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend\".split(\" \"), function( i, o ) {\n\tjQuery.fn[o] = function( f ) {\n\t\treturn this.bind(o, f);\n\t};\n});\n\njQuery.extend({\n\n\tget: function( url, data, callback, type ) {\n\t\t// shift arguments if data argument was omited\n\t\tif ( jQuery.isFunction( data ) ) {\n\t\t\ttype = type || callback;\n\t\t\tcallback = data;\n\t\t\tdata = null;\n\t\t}\n\n\t\treturn jQuery.ajax({\n\t\t\ttype: \"GET\",\n\t\t\turl: url,\n\t\t\tdata: data,\n\t\t\tsuccess: callback,\n\t\t\tdataType: type\n\t\t});\n\t},\n\n\tgetScript: function( url, callback ) {\n\t\treturn jQuery.get(url, null, callback, \"script\");\n\t},\n\n\tgetJSON: function( url, data, callback ) {\n\t\treturn jQuery.get(url, data, callback, \"json\");\n\t},\n\n\tpost: function( url, data, callback, type ) {\n\t\t// shift arguments if data argument was omited\n\t\tif ( jQuery.isFunction( data ) ) {\n\t\t\ttype = type || callback;\n\t\t\tcallback = data;\n\t\t\tdata = {};\n\t\t}\n\n\t\treturn jQuery.ajax({\n\t\t\ttype: \"POST\",\n\t\t\turl: url,\n\t\t\tdata: data,\n\t\t\tsuccess: callback,\n\t\t\tdataType: type\n\t\t});\n\t},\n\n\tajaxSetup: function( settings ) {\n\t\tjQuery.extend( jQuery.ajaxSettings, settings );\n\t},\n\n\tajaxSettings: {\n\t\turl: location.href,\n\t\tglobal: true,\n\t\ttype: \"GET\",\n\t\tcontentType: \"application/x-www-form-urlencoded\",\n\t\tprocessData: true,\n\t\tasync: true,\n\t\t/*\n\t\ttimeout: 0,\n\t\tdata: null,\n\t\tusername: null,\n\t\tpassword: null,\n\t\ttraditional: false,\n\t\t*/\n\t\t// Create the request object; Microsoft failed to properly\n\t\t// implement the XMLHttpRequest in IE7 (can't request local files),\n\t\t// so we use the ActiveXObject when it is available\n\t\t// This function can be overriden by calling jQuery.ajaxSetup\n\t\txhr: window.XMLHttpRequest && (window.location.protocol !== \"file:\" || !window.ActiveXObject) ?\n\t\t\tfunction() {\n\t\t\t\treturn new window.XMLHttpRequest();\n\t\t\t} :\n\t\t\tfunction() {\n\t\t\t\ttry {\n\t\t\t\t\treturn new window.ActiveXObject(\"Microsoft.XMLHTTP\");\n\t\t\t\t} catch(e) {}\n\t\t\t},\n\t\taccepts: {\n\t\t\txml: \"application/xml, text/xml\",\n\t\t\thtml: \"text/html\",\n\t\t\tscript: \"text/javascript, application/javascript\",\n\t\t\tjson: \"application/json, text/javascript\",\n\t\t\ttext: \"text/plain\",\n\t\t\t_default: \"*/*\"\n\t\t}\n\t},\n\n\t// Last-Modified header cache for next request\n\tlastModified: {},\n\tetag: {},\n\n\tajax: function( origSettings ) {\n\t\tvar s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);\n\t\t\n\t\tvar jsonp, status, data,\n\t\t\tcallbackContext = origSettings && origSettings.context || s,\n\t\t\ttype = s.type.toUpperCase();\n\n\t\t// convert data if not already a string\n\t\tif ( s.data && s.processData && typeof s.data !== \"string\" ) {\n\t\t\ts.data = jQuery.param( s.data, s.traditional );\n\t\t}\n\n\t\t// Handle JSONP Parameter Callbacks\n\t\tif ( s.dataType === \"jsonp\" ) {\n\t\t\tif ( type === \"GET\" ) {\n\t\t\t\tif ( !jsre.test( s.url ) ) {\n\t\t\t\t\ts.url += (rquery.test( s.url ) ? \"&\" : \"?\") + (s.jsonp || \"callback\") + \"=?\";\n\t\t\t\t}\n\t\t\t} else if ( !s.data || !jsre.test(s.data) ) {\n\t\t\t\ts.data = (s.data ? s.data + \"&\" : \"\") + (s.jsonp || \"callback\") + \"=?\";\n\t\t\t}\n\t\t\ts.dataType = \"json\";\n\t\t}\n\n\t\t// Build temporary JSONP function\n\t\tif ( s.dataType === \"json\" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {\n\t\t\tjsonp = s.jsonpCallback || (\"jsonp\" + jsc++);\n\n\t\t\t// Replace the =? sequence both in the query string and the data\n\t\t\tif ( s.data ) {\n\t\t\t\ts.data = (s.data + \"\").replace(jsre, \"=\" + jsonp + \"$1\");\n\t\t\t}\n\n\t\t\ts.url = s.url.replace(jsre, \"=\" + jsonp + \"$1\");\n\n\t\t\t// We need to make sure\n\t\t\t// that a JSONP style response is executed properly\n\t\t\ts.dataType = \"script\";\n\n\t\t\t// Handle JSONP-style loading\n\t\t\twindow[ jsonp ] = window[ jsonp ] || function( tmp ) {\n\t\t\t\tdata = tmp;\n\t\t\t\tsuccess();\n\t\t\t\tcomplete();\n\t\t\t\t// Garbage collect\n\t\t\t\twindow[ jsonp ] = undefined;\n\n\t\t\t\ttry {\n\t\t\t\t\tdelete window[ jsonp ];\n\t\t\t\t} catch(e) {}\n\n\t\t\t\tif ( head ) {\n\t\t\t\t\thead.removeChild( script );\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\tif ( s.dataType === \"script\" && s.cache === null ) {\n\t\t\ts.cache = false;\n\t\t}\n\n\t\tif ( s.cache === false && type === \"GET\" ) {\n\t\t\tvar ts = now();\n\n\t\t\t// try replacing _= if it is there\n\t\t\tvar ret = s.url.replace(rts, \"$1_=\" + ts + \"$2\");\n\n\t\t\t// if nothing was replaced, add timestamp to the end\n\t\t\ts.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? \"&\" : \"?\") + \"_=\" + ts : \"\");\n\t\t}\n\n\t\t// If data is available, append data to url for get requests\n\t\tif ( s.data && type === \"GET\" ) {\n\t\t\ts.url += (rquery.test(s.url) ? \"&\" : \"?\") + s.data;\n\t\t}\n\n\t\t// Watch for a new set of requests\n\t\tif ( s.global && ! jQuery.active++ ) {\n\t\t\tjQuery.event.trigger( \"ajaxStart\" );\n\t\t}\n\n\t\t// Matches an absolute URL, and saves the domain\n\t\tvar parts = rurl.exec( s.url ),\n\t\t\tremote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);\n\n\t\t// If we're requesting a remote document\n\t\t// and trying to load JSON or Script with a GET\n\t\tif ( s.dataType === \"script\" && type === \"GET\" && remote ) {\n\t\t\tvar head = document.getElementsByTagName(\"head\")[0] || document.documentElement;\n\t\t\tvar script = document.createElement(\"script\");\n\t\t\tscript.src = s.url;\n\t\t\tif ( s.scriptCharset ) {\n\t\t\t\tscript.charset = s.scriptCharset;\n\t\t\t}\n\n\t\t\t// Handle Script loading\n\t\t\tif ( !jsonp ) {\n\t\t\t\tvar done = false;\n\n\t\t\t\t// Attach handlers for all browsers\n\t\t\t\tscript.onload = script.onreadystatechange = function() {\n\t\t\t\t\tif ( !done && (!this.readyState ||\n\t\t\t\t\t\t\tthis.readyState === \"loaded\" || this.readyState === \"complete\") ) {\n\t\t\t\t\t\tdone = true;\n\t\t\t\t\t\tsuccess();\n\t\t\t\t\t\tcomplete();\n\n\t\t\t\t\t\t// Handle memory leak in IE\n\t\t\t\t\t\tscript.onload = script.onreadystatechange = null;\n\t\t\t\t\t\tif ( head && script.parentNode ) {\n\t\t\t\t\t\t\thead.removeChild( script );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Use insertBefore instead of appendChild  to circumvent an IE6 bug.\n\t\t\t// This arises when a base node is used (#2709 and #4378).\n\t\t\thead.insertBefore( script, head.firstChild );\n\n\t\t\t// We handle everything using the script element injection\n\t\t\treturn undefined;\n\t\t}\n\n\t\tvar requestDone = false;\n\n\t\t// Create the request object\n\t\tvar xhr = s.xhr();\n\n\t\tif ( !xhr ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Open the socket\n\t\t// Passing null username, generates a login popup on Opera (#2865)\n\t\tif ( s.username ) {\n\t\t\txhr.open(type, s.url, s.async, s.username, s.password);\n\t\t} else {\n\t\t\txhr.open(type, s.url, s.async);\n\t\t}\n\n\t\t// Need an extra try/catch for cross domain requests in Firefox 3\n\t\ttry {\n\t\t\t// Set the correct header, if data is being sent\n\t\t\tif ( s.data || origSettings && origSettings.contentType ) {\n\t\t\t\txhr.setRequestHeader(\"Content-Type\", s.contentType);\n\t\t\t}\n\n\t\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\t\tif ( s.ifModified ) {\n\t\t\t\tif ( jQuery.lastModified[s.url] ) {\n\t\t\t\t\txhr.setRequestHeader(\"If-Modified-Since\", jQuery.lastModified[s.url]);\n\t\t\t\t}\n\n\t\t\t\tif ( jQuery.etag[s.url] ) {\n\t\t\t\t\txhr.setRequestHeader(\"If-None-Match\", jQuery.etag[s.url]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set header so the called script knows that it's an XMLHttpRequest\n\t\t\t// Only send the header if it's not a remote XHR\n\t\t\tif ( !remote ) {\n\t\t\t\txhr.setRequestHeader(\"X-Requested-With\", \"XMLHttpRequest\");\n\t\t\t}\n\n\t\t\t// Set the Accepts header for the server, depending on the dataType\n\t\t\txhr.setRequestHeader(\"Accept\", s.dataType && s.accepts[ s.dataType ] ?\n\t\t\t\ts.accepts[ s.dataType ] + \", */*\" :\n\t\t\t\ts.accepts._default );\n\t\t} catch(e) {}\n\n\t\t// Allow custom headers/mimetypes and early abort\n\t\tif ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) {\n\t\t\t// Handle the global AJAX counter\n\t\t\tif ( s.global && ! --jQuery.active ) {\n\t\t\t\tjQuery.event.trigger( \"ajaxStop\" );\n\t\t\t}\n\n\t\t\t// close opended socket\n\t\t\txhr.abort();\n\t\t\treturn false;\n\t\t}\n\n\t\tif ( s.global ) {\n\t\t\ttrigger(\"ajaxSend\", [xhr, s]);\n\t\t}\n\n\t\t// Wait for a response to come back\n\t\tvar onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {\n\t\t\t// The request was aborted\n\t\t\tif ( !xhr || xhr.readyState === 0 || isTimeout === \"abort\" ) {\n\t\t\t\t// Opera doesn't call onreadystatechange before this point\n\t\t\t\t// so we simulate the call\n\t\t\t\tif ( !requestDone ) {\n\t\t\t\t\tcomplete();\n\t\t\t\t}\n\n\t\t\t\trequestDone = true;\n\t\t\t\tif ( xhr ) {\n\t\t\t\t\txhr.onreadystatechange = jQuery.noop;\n\t\t\t\t}\n\n\t\t\t// The transfer is complete and the data is available, or the request timed out\n\t\t\t} else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === \"timeout\") ) {\n\t\t\t\trequestDone = true;\n\t\t\t\txhr.onreadystatechange = jQuery.noop;\n\n\t\t\t\tstatus = isTimeout === \"timeout\" ?\n\t\t\t\t\t\"timeout\" :\n\t\t\t\t\t!jQuery.httpSuccess( xhr ) ?\n\t\t\t\t\t\t\"error\" :\n\t\t\t\t\t\ts.ifModified && jQuery.httpNotModified( xhr, s.url ) ?\n\t\t\t\t\t\t\t\"notmodified\" :\n\t\t\t\t\t\t\t\"success\";\n\n\t\t\t\tvar errMsg;\n\n\t\t\t\tif ( status === \"success\" ) {\n\t\t\t\t\t// Watch for, and catch, XML document parse errors\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// process the data (runs the xml through httpData regardless of callback)\n\t\t\t\t\t\tdata = jQuery.httpData( xhr, s.dataType, s );\n\t\t\t\t\t} catch(err) {\n\t\t\t\t\t\tstatus = \"parsererror\";\n\t\t\t\t\t\terrMsg = err;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Make sure that the request was successful or notmodified\n\t\t\t\tif ( status === \"success\" || status === \"notmodified\" ) {\n\t\t\t\t\t// JSONP handles its own success callback\n\t\t\t\t\tif ( !jsonp ) {\n\t\t\t\t\t\tsuccess();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tjQuery.handleError(s, xhr, status, errMsg);\n\t\t\t\t}\n\n\t\t\t\t// Fire the complete handlers\n\t\t\t\tcomplete();\n\n\t\t\t\tif ( isTimeout === \"timeout\" ) {\n\t\t\t\t\txhr.abort();\n\t\t\t\t}\n\n\t\t\t\t// Stop memory leaks\n\t\t\t\tif ( s.async ) {\n\t\t\t\t\txhr = null;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Override the abort handler, if we can (IE doesn't allow it, but that's OK)\n\t\t// Opera doesn't fire onreadystatechange at all on abort\n\t\ttry {\n\t\t\tvar oldAbort = xhr.abort;\n\t\t\txhr.abort = function() {\n\t\t\t\tif ( xhr ) {\n\t\t\t\t\toldAbort.call( xhr );\n\t\t\t\t}\n\n\t\t\t\tonreadystatechange( \"abort\" );\n\t\t\t};\n\t\t} catch(e) { }\n\n\t\t// Timeout checker\n\t\tif ( s.async && s.timeout > 0 ) {\n\t\t\tsetTimeout(function() {\n\t\t\t\t// Check to see if the request is still happening\n\t\t\t\tif ( xhr && !requestDone ) {\n\t\t\t\t\tonreadystatechange( \"timeout\" );\n\t\t\t\t}\n\t\t\t}, s.timeout);\n\t\t}\n\n\t\t// Send the data\n\t\ttry {\n\t\t\txhr.send( type === \"POST\" || type === \"PUT\" || type === \"DELETE\" ? s.data : null );\n\t\t} catch(e) {\n\t\t\tjQuery.handleError(s, xhr, null, e);\n\t\t\t// Fire the complete handlers\n\t\t\tcomplete();\n\t\t}\n\n\t\t// firefox 1.5 doesn't fire statechange for sync requests\n\t\tif ( !s.async ) {\n\t\t\tonreadystatechange();\n\t\t}\n\n\t\tfunction success() {\n\t\t\t// If a local callback was specified, fire it and pass it the data\n\t\t\tif ( s.success ) {\n\t\t\t\ts.success.call( callbackContext, data, status, xhr );\n\t\t\t}\n\n\t\t\t// Fire the global callback\n\t\t\tif ( s.global ) {\n\t\t\t\ttrigger( \"ajaxSuccess\", [xhr, s] );\n\t\t\t}\n\t\t}\n\n\t\tfunction complete() {\n\t\t\t// Process result\n\t\t\tif ( s.complete ) {\n\t\t\t\ts.complete.call( callbackContext, xhr, status);\n\t\t\t}\n\n\t\t\t// The request was completed\n\t\t\tif ( s.global ) {\n\t\t\t\ttrigger( \"ajaxComplete\", [xhr, s] );\n\t\t\t}\n\n\t\t\t// Handle the global AJAX counter\n\t\t\tif ( s.global && ! --jQuery.active ) {\n\t\t\t\tjQuery.event.trigger( \"ajaxStop\" );\n\t\t\t}\n\t\t}\n\t\t\n\t\tfunction trigger(type, args) {\n\t\t\t(s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);\n\t\t}\n\n\t\t// return XMLHttpRequest to allow aborting the request etc.\n\t\treturn xhr;\n\t},\n\n\thandleError: function( s, xhr, status, e ) {\n\t\t// If a local callback was specified, fire it\n\t\tif ( s.error ) {\n\t\t\ts.error.call( s.context || s, xhr, status, e );\n\t\t}\n\n\t\t// Fire the global callback\n\t\tif ( s.global ) {\n\t\t\t(s.context ? jQuery(s.context) : jQuery.event).trigger( \"ajaxError\", [xhr, s, e] );\n\t\t}\n\t},\n\n\t// Counter for holding the number of active queries\n\tactive: 0,\n\n\t// Determines if an XMLHttpRequest was successful or not\n\thttpSuccess: function( xhr ) {\n\t\ttry {\n\t\t\t// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450\n\t\t\treturn !xhr.status && location.protocol === \"file:\" ||\n\t\t\t\t// Opera returns 0 when status is 304\n\t\t\t\t( xhr.status >= 200 && xhr.status < 300 ) ||\n\t\t\t\txhr.status === 304 || xhr.status === 1223 || xhr.status === 0;\n\t\t} catch(e) {}\n\n\t\treturn false;\n\t},\n\n\t// Determines if an XMLHttpRequest returns NotModified\n\thttpNotModified: function( xhr, url ) {\n\t\tvar lastModified = xhr.getResponseHeader(\"Last-Modified\"),\n\t\t\tetag = xhr.getResponseHeader(\"Etag\");\n\n\t\tif ( lastModified ) {\n\t\t\tjQuery.lastModified[url] = lastModified;\n\t\t}\n\n\t\tif ( etag ) {\n\t\t\tjQuery.etag[url] = etag;\n\t\t}\n\n\t\t// Opera returns 0 when status is 304\n\t\treturn xhr.status === 304 || xhr.status === 0;\n\t},\n\n\thttpData: function( xhr, type, s ) {\n\t\tvar ct = xhr.getResponseHeader(\"content-type\") || \"\",\n\t\t\txml = type === \"xml\" || !type && ct.indexOf(\"xml\") >= 0,\n\t\t\tdata = xml ? xhr.responseXML : xhr.responseText;\n\n\t\tif ( xml && data.documentElement.nodeName === \"parsererror\" ) {\n\t\t\tjQuery.error( \"parsererror\" );\n\t\t}\n\n\t\t// Allow a pre-filtering function to sanitize the response\n\t\t// s is checked to keep backwards compatibility\n\t\tif ( s && s.dataFilter ) {\n\t\t\tdata = s.dataFilter( data, type );\n\t\t}\n\n\t\t// The filter can actually parse the response\n\t\tif ( typeof data === \"string\" ) {\n\t\t\t// Get the JavaScript object, if JSON is used.\n\t\t\tif ( type === \"json\" || !type && ct.indexOf(\"json\") >= 0 ) {\n\t\t\t\tdata = jQuery.parseJSON( data );\n\n\t\t\t// If the type is \"script\", eval it in global context\n\t\t\t} else if ( type === \"script\" || !type && ct.indexOf(\"javascript\") >= 0 ) {\n\t\t\t\tjQuery.globalEval( data );\n\t\t\t}\n\t\t}\n\n\t\treturn data;\n\t},\n\n\t// Serialize an array of form elements or a set of\n\t// key/values into a query string\n\tparam: function( a, traditional ) {\n\t\tvar s = [];\n\t\t\n\t\t// Set traditional to true for jQuery <= 1.3.2 behavior.\n\t\tif ( traditional === undefined ) {\n\t\t\ttraditional = jQuery.ajaxSettings.traditional;\n\t\t}\n\t\t\n\t\t// If an array was passed in, assume that it is an array of form elements.\n\t\tif ( jQuery.isArray(a) || a.jquery ) {\n\t\t\t// Serialize the form elements\n\t\t\tjQuery.each( a, function() {\n\t\t\t\tadd( this.name, this.value );\n\t\t\t});\n\t\t\t\n\t\t} else {\n\t\t\t// If traditional, encode the \"old\" way (the way 1.3.2 or older\n\t\t\t// did it), otherwise encode params recursively.\n\t\t\tfor ( var prefix in a ) {\n\t\t\t\tbuildParams( prefix, a[prefix] );\n\t\t\t}\n\t\t}\n\n\t\t// Return the resulting serialization\n\t\treturn s.join(\"&\").replace(r20, \"+\");\n\n\t\tfunction buildParams( prefix, obj ) {\n\t\t\tif ( jQuery.isArray(obj) ) {\n\t\t\t\t// Serialize array item.\n\t\t\t\tjQuery.each( obj, function( i, v ) {\n\t\t\t\t\tif ( traditional || /\\[\\]$/.test( prefix ) ) {\n\t\t\t\t\t\t// Treat each array item as a scalar.\n\t\t\t\t\t\tadd( prefix, v );\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If array item is non-scalar (array or object), encode its\n\t\t\t\t\t\t// numeric index to resolve deserialization ambiguity issues.\n\t\t\t\t\t\t// Note that rack (as of 1.0.0) can't currently deserialize\n\t\t\t\t\t\t// nested arrays properly, and attempting to do so may cause\n\t\t\t\t\t\t// a server error. Possible fixes are to modify rack's\n\t\t\t\t\t\t// deserialization algorithm or to provide an option or flag\n\t\t\t\t\t\t// to force array serialization to be shallow.\n\t\t\t\t\t\tbuildParams( prefix + \"[\" + ( typeof v === \"object\" || jQuery.isArray(v) ? i : \"\" ) + \"]\", v );\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\t\t\n\t\t\t} else if ( !traditional && obj != null && typeof obj === \"object\" ) {\n\t\t\t\t// Serialize object item.\n\t\t\t\tjQuery.each( obj, function( k, v ) {\n\t\t\t\t\tbuildParams( prefix + \"[\" + k + \"]\", v );\n\t\t\t\t});\n\t\t\t\t\t\n\t\t\t} else {\n\t\t\t\t// Serialize scalar item.\n\t\t\t\tadd( prefix, obj );\n\t\t\t}\n\t\t}\n\n\t\tfunction add( key, value ) {\n\t\t\t// If value is a function, invoke it and return its value\n\t\t\tvalue = jQuery.isFunction(value) ? value() : value;\n\t\t\ts[ s.length ] = encodeURIComponent(key) + \"=\" + encodeURIComponent(value);\n\t\t}\n\t}\n});\nvar elemdisplay = {},\n\trfxtypes = /toggle|show|hide/,\n\trfxnum = /^([+-]=)?([\\d+-.]+)(.*)$/,\n\ttimerId,\n\tfxAttrs = [\n\t\t// height animations\n\t\t[ \"height\", \"marginTop\", \"marginBottom\", \"paddingTop\", \"paddingBottom\" ],\n\t\t// width animations\n\t\t[ \"width\", \"marginLeft\", \"marginRight\", \"paddingLeft\", \"paddingRight\" ],\n\t\t// opacity animations\n\t\t[ \"opacity\" ]\n\t];\n\njQuery.fn.extend({\n\tshow: function( speed, callback ) {\n\t\tif ( speed || speed === 0) {\n\t\t\treturn this.animate( genFx(\"show\", 3), speed, callback);\n\n\t\t} else {\n\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\tvar old = jQuery.data(this[i], \"olddisplay\");\n\n\t\t\t\tthis[i].style.display = old || \"\";\n\n\t\t\t\tif ( jQuery.css(this[i], \"display\") === \"none\" ) {\n\t\t\t\t\tvar nodeName = this[i].nodeName, display;\n\n\t\t\t\t\tif ( elemdisplay[ nodeName ] ) {\n\t\t\t\t\t\tdisplay = elemdisplay[ nodeName ];\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar elem = jQuery(\"<\" + nodeName + \" />\").appendTo(\"body\");\n\n\t\t\t\t\t\tdisplay = elem.css(\"display\");\n\n\t\t\t\t\t\tif ( display === \"none\" ) {\n\t\t\t\t\t\t\tdisplay = \"block\";\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\telem.remove();\n\n\t\t\t\t\t\telemdisplay[ nodeName ] = display;\n\t\t\t\t\t}\n\n\t\t\t\t\tjQuery.data(this[i], \"olddisplay\", display);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set the display of the elements in a second loop\n\t\t\t// to avoid the constant reflow\n\t\t\tfor ( var j = 0, k = this.length; j < k; j++ ) {\n\t\t\t\tthis[j].style.display = jQuery.data(this[j], \"olddisplay\") || \"\";\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t},\n\n\thide: function( speed, callback ) {\n\t\tif ( speed || speed === 0 ) {\n\t\t\treturn this.animate( genFx(\"hide\", 3), speed, callback);\n\n\t\t} else {\n\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\tvar old = jQuery.data(this[i], \"olddisplay\");\n\t\t\t\tif ( !old && old !== \"none\" ) {\n\t\t\t\t\tjQuery.data(this[i], \"olddisplay\", jQuery.css(this[i], \"display\"));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set the display of the elements in a second loop\n\t\t\t// to avoid the constant reflow\n\t\t\tfor ( var j = 0, k = this.length; j < k; j++ ) {\n\t\t\t\tthis[j].style.display = \"none\";\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t},\n\n\t// Save the old toggle function\n\t_toggle: jQuery.fn.toggle,\n\n\ttoggle: function( fn, fn2 ) {\n\t\tvar bool = typeof fn === \"boolean\";\n\n\t\tif ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {\n\t\t\tthis._toggle.apply( this, arguments );\n\n\t\t} else if ( fn == null || bool ) {\n\t\t\tthis.each(function() {\n\t\t\t\tvar state = bool ? fn : jQuery(this).is(\":hidden\");\n\t\t\t\tjQuery(this)[ state ? \"show\" : \"hide\" ]();\n\t\t\t});\n\n\t\t} else {\n\t\t\tthis.animate(genFx(\"toggle\", 3), fn, fn2);\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tfadeTo: function( speed, to, callback ) {\n\t\treturn this.filter(\":hidden\").css(\"opacity\", 0).show().end()\n\t\t\t\t\t.animate({opacity: to}, speed, callback);\n\t},\n\n\tanimate: function( prop, speed, easing, callback ) {\n\t\tvar optall = jQuery.speed(speed, easing, callback);\n\n\t\tif ( jQuery.isEmptyObject( prop ) ) {\n\t\t\treturn this.each( optall.complete );\n\t\t}\n\n\t\treturn this[ optall.queue === false ? \"each\" : \"queue\" ](function() {\n\t\t\tvar opt = jQuery.extend({}, optall), p,\n\t\t\t\thidden = this.nodeType === 1 && jQuery(this).is(\":hidden\"),\n\t\t\t\tself = this;\n\n\t\t\tfor ( p in prop ) {\n\t\t\t\tvar name = p.replace(rdashAlpha, fcamelCase);\n\n\t\t\t\tif ( p !== name ) {\n\t\t\t\t\tprop[ name ] = prop[ p ];\n\t\t\t\t\tdelete prop[ p ];\n\t\t\t\t\tp = name;\n\t\t\t\t}\n\n\t\t\t\tif ( prop[p] === \"hide\" && hidden || prop[p] === \"show\" && !hidden ) {\n\t\t\t\t\treturn opt.complete.call(this);\n\t\t\t\t}\n\n\t\t\t\tif ( ( p === \"height\" || p === \"width\" ) && this.style ) {\n\t\t\t\t\t// Store display property\n\t\t\t\t\topt.display = jQuery.css(this, \"display\");\n\n\t\t\t\t\t// Make sure that nothing sneaks out\n\t\t\t\t\topt.overflow = this.style.overflow;\n\t\t\t\t}\n\n\t\t\t\tif ( jQuery.isArray( prop[p] ) ) {\n\t\t\t\t\t// Create (if needed) and add to specialEasing\n\t\t\t\t\t(opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];\n\t\t\t\t\tprop[p] = prop[p][0];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( opt.overflow != null ) {\n\t\t\t\tthis.style.overflow = \"hidden\";\n\t\t\t}\n\n\t\t\topt.curAnim = jQuery.extend({}, prop);\n\n\t\t\tjQuery.each( prop, function( name, val ) {\n\t\t\t\tvar e = new jQuery.fx( self, opt, name );\n\n\t\t\t\tif ( rfxtypes.test(val) ) {\n\t\t\t\t\te[ val === \"toggle\" ? hidden ? \"show\" : \"hide\" : val ]( prop );\n\n\t\t\t\t} else {\n\t\t\t\t\tvar parts = rfxnum.exec(val),\n\t\t\t\t\t\tstart = e.cur(true) || 0;\n\n\t\t\t\t\tif ( parts ) {\n\t\t\t\t\t\tvar end = parseFloat( parts[2] ),\n\t\t\t\t\t\t\tunit = parts[3] || \"px\";\n\n\t\t\t\t\t\t// We need to compute starting value\n\t\t\t\t\t\tif ( unit !== \"px\" ) {\n\t\t\t\t\t\t\tself.style[ name ] = (end || 1) + unit;\n\t\t\t\t\t\t\tstart = ((end || 1) / e.cur(true)) * start;\n\t\t\t\t\t\t\tself.style[ name ] = start + unit;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If a +=/-= token was provided, we're doing a relative animation\n\t\t\t\t\t\tif ( parts[1] ) {\n\t\t\t\t\t\t\tend = ((parts[1] === \"-=\" ? -1 : 1) * end) + start;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\te.custom( start, end, unit );\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\te.custom( start, val, \"\" );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// For JS strict compliance\n\t\t\treturn true;\n\t\t});\n\t},\n\n\tstop: function( clearQueue, gotoEnd ) {\n\t\tvar timers = jQuery.timers;\n\n\t\tif ( clearQueue ) {\n\t\t\tthis.queue([]);\n\t\t}\n\n\t\tthis.each(function() {\n\t\t\t// go in reverse order so anything added to the queue during the loop is ignored\n\t\t\tfor ( var i = timers.length - 1; i >= 0; i-- ) {\n\t\t\t\tif ( timers[i].elem === this ) {\n\t\t\t\t\tif (gotoEnd) {\n\t\t\t\t\t\t// force the next step to be the last\n\t\t\t\t\t\ttimers[i](true);\n\t\t\t\t\t}\n\n\t\t\t\t\ttimers.splice(i, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// start the next in the queue if the last step wasn't forced\n\t\tif ( !gotoEnd ) {\n\t\t\tthis.dequeue();\n\t\t}\n\n\t\treturn this;\n\t}\n\n});\n\n// Generate shortcuts for custom animations\njQuery.each({\n\tslideDown: genFx(\"show\", 1),\n\tslideUp: genFx(\"hide\", 1),\n\tslideToggle: genFx(\"toggle\", 1),\n\tfadeIn: { opacity: \"show\" },\n\tfadeOut: { opacity: \"hide\" }\n}, function( name, props ) {\n\tjQuery.fn[ name ] = function( speed, callback ) {\n\t\treturn this.animate( props, speed, callback );\n\t};\n});\n\njQuery.extend({\n\tspeed: function( speed, easing, fn ) {\n\t\tvar opt = speed && typeof speed === \"object\" ? speed : {\n\t\t\tcomplete: fn || !fn && easing ||\n\t\t\t\tjQuery.isFunction( speed ) && speed,\n\t\t\tduration: speed,\n\t\t\teasing: fn && easing || easing && !jQuery.isFunction(easing) && easing\n\t\t};\n\n\t\topt.duration = jQuery.fx.off ? 0 : typeof opt.duration === \"number\" ? opt.duration :\n\t\t\tjQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;\n\n\t\t// Queueing\n\t\topt.old = opt.complete;\n\t\topt.complete = function() {\n\t\t\tif ( opt.queue !== false ) {\n\t\t\t\tjQuery(this).dequeue();\n\t\t\t}\n\t\t\tif ( jQuery.isFunction( opt.old ) ) {\n\t\t\t\topt.old.call( this );\n\t\t\t}\n\t\t};\n\n\t\treturn opt;\n\t},\n\n\teasing: {\n\t\tlinear: function( p, n, firstNum, diff ) {\n\t\t\treturn firstNum + diff * p;\n\t\t},\n\t\tswing: function( p, n, firstNum, diff ) {\n\t\t\treturn ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;\n\t\t}\n\t},\n\n\ttimers: [],\n\n\tfx: function( elem, options, prop ) {\n\t\tthis.options = options;\n\t\tthis.elem = elem;\n\t\tthis.prop = prop;\n\n\t\tif ( !options.orig ) {\n\t\t\toptions.orig = {};\n\t\t}\n\t}\n\n});\n\njQuery.fx.prototype = {\n\t// Simple function for setting a style value\n\tupdate: function() {\n\t\tif ( this.options.step ) {\n\t\t\tthis.options.step.call( this.elem, this.now, this );\n\t\t}\n\n\t\t(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );\n\n\t\t// Set display property to block for height/width animations\n\t\tif ( ( this.prop === \"height\" || this.prop === \"width\" ) && this.elem.style ) {\n\t\t\tthis.elem.style.display = \"block\";\n\t\t}\n\t},\n\n\t// Get the current size\n\tcur: function( force ) {\n\t\tif ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {\n\t\t\treturn this.elem[ this.prop ];\n\t\t}\n\n\t\tvar r = parseFloat(jQuery.css(this.elem, this.prop, force));\n\t\treturn r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;\n\t},\n\n\t// Start an animation from one number to another\n\tcustom: function( from, to, unit ) {\n\t\tthis.startTime = now();\n\t\tthis.start = from;\n\t\tthis.end = to;\n\t\tthis.unit = unit || this.unit || \"px\";\n\t\tthis.now = this.start;\n\t\tthis.pos = this.state = 0;\n\n\t\tvar self = this;\n\t\tfunction t( gotoEnd ) {\n\t\t\treturn self.step(gotoEnd);\n\t\t}\n\n\t\tt.elem = this.elem;\n\n\t\tif ( t() && jQuery.timers.push(t) && !timerId ) {\n\t\t\ttimerId = setInterval(jQuery.fx.tick, 13);\n\t\t}\n\t},\n\n\t// Simple 'show' function\n\tshow: function() {\n\t\t// Remember where we started, so that we can go back to it later\n\t\tthis.options.orig[this.prop] = jQuery.style( this.elem, this.prop );\n\t\tthis.options.show = true;\n\n\t\t// Begin the animation\n\t\t// Make sure that we start at a small width/height to avoid any\n\t\t// flash of content\n\t\tthis.custom(this.prop === \"width\" || this.prop === \"height\" ? 1 : 0, this.cur());\n\n\t\t// Start by showing the element\n\t\tjQuery( this.elem ).show();\n\t},\n\n\t// Simple 'hide' function\n\thide: function() {\n\t\t// Remember where we started, so that we can go back to it later\n\t\tthis.options.orig[this.prop] = jQuery.style( this.elem, this.prop );\n\t\tthis.options.hide = true;\n\n\t\t// Begin the animation\n\t\tthis.custom(this.cur(), 0);\n\t},\n\n\t// Each step of an animation\n\tstep: function( gotoEnd ) {\n\t\tvar t = now(), done = true;\n\n\t\tif ( gotoEnd || t >= this.options.duration + this.startTime ) {\n\t\t\tthis.now = this.end;\n\t\t\tthis.pos = this.state = 1;\n\t\t\tthis.update();\n\n\t\t\tthis.options.curAnim[ this.prop ] = true;\n\n\t\t\tfor ( var i in this.options.curAnim ) {\n\t\t\t\tif ( this.options.curAnim[i] !== true ) {\n\t\t\t\t\tdone = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( done ) {\n\t\t\t\tif ( this.options.display != null ) {\n\t\t\t\t\t// Reset the overflow\n\t\t\t\t\tthis.elem.style.overflow = this.options.overflow;\n\n\t\t\t\t\t// Reset the display\n\t\t\t\t\tvar old = jQuery.data(this.elem, \"olddisplay\");\n\t\t\t\t\tthis.elem.style.display = old ? old : this.options.display;\n\n\t\t\t\t\tif ( jQuery.css(this.elem, \"display\") === \"none\" ) {\n\t\t\t\t\t\tthis.elem.style.display = \"block\";\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Hide the element if the \"hide\" operation was done\n\t\t\t\tif ( this.options.hide ) {\n\t\t\t\t\tjQuery(this.elem).hide();\n\t\t\t\t}\n\n\t\t\t\t// Reset the properties, if the item has been hidden or shown\n\t\t\t\tif ( this.options.hide || this.options.show ) {\n\t\t\t\t\tfor ( var p in this.options.curAnim ) {\n\t\t\t\t\t\tjQuery.style(this.elem, p, this.options.orig[p]);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Execute the complete function\n\t\t\t\tthis.options.complete.call( this.elem );\n\t\t\t}\n\n\t\t\treturn false;\n\n\t\t} else {\n\t\t\tvar n = t - this.startTime;\n\t\t\tthis.state = n / this.options.duration;\n\n\t\t\t// Perform the easing function, defaults to swing\n\t\t\tvar specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];\n\t\t\tvar defaultEasing = this.options.easing || (jQuery.easing.swing ? \"swing\" : \"linear\");\n\t\t\tthis.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);\n\t\t\tthis.now = this.start + ((this.end - this.start) * this.pos);\n\n\t\t\t// Perform the next step of the animation\n\t\t\tthis.update();\n\t\t}\n\n\t\treturn true;\n\t}\n};\n\njQuery.extend( jQuery.fx, {\n\ttick: function() {\n\t\tvar timers = jQuery.timers;\n\n\t\tfor ( var i = 0; i < timers.length; i++ ) {\n\t\t\tif ( !timers[i]() ) {\n\t\t\t\ttimers.splice(i--, 1);\n\t\t\t}\n\t\t}\n\n\t\tif ( !timers.length ) {\n\t\t\tjQuery.fx.stop();\n\t\t}\n\t},\n\t\t\n\tstop: function() {\n\t\tclearInterval( timerId );\n\t\ttimerId = null;\n\t},\n\t\n\tspeeds: {\n\t\tslow: 600,\n \t\tfast: 200,\n \t\t// Default speed\n \t\t_default: 400\n\t},\n\n\tstep: {\n\t\topacity: function( fx ) {\n\t\t\tjQuery.style(fx.elem, \"opacity\", fx.now);\n\t\t},\n\n\t\t_default: function( fx ) {\n\t\t\tif ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {\n\t\t\t\tfx.elem.style[ fx.prop ] = (fx.prop === \"width\" || fx.prop === \"height\" ? Math.max(0, fx.now) : fx.now) + fx.unit;\n\t\t\t} else {\n\t\t\t\tfx.elem[ fx.prop ] = fx.now;\n\t\t\t}\n\t\t}\n\t}\n});\n\nif ( jQuery.expr && jQuery.expr.filters ) {\n\tjQuery.expr.filters.animated = function( elem ) {\n\t\treturn jQuery.grep(jQuery.timers, function( fn ) {\n\t\t\treturn elem === fn.elem;\n\t\t}).length;\n\t};\n}\n\nfunction genFx( type, num ) {\n\tvar obj = {};\n\n\tjQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {\n\t\tobj[ this ] = type;\n\t});\n\n\treturn obj;\n}\nif ( \"getBoundingClientRect\" in document.documentElement ) {\n\tjQuery.fn.offset = function( options ) {\n\t\tvar elem = this[0];\n\n\t\tif ( options ) { \n\t\t\treturn this.each(function( i ) {\n\t\t\t\tjQuery.offset.setOffset( this, options, i );\n\t\t\t});\n\t\t}\n\n\t\tif ( !elem || !elem.ownerDocument ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif ( elem === elem.ownerDocument.body ) {\n\t\t\treturn jQuery.offset.bodyOffset( elem );\n\t\t}\n\n\t\tvar box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement,\n\t\t\tclientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,\n\t\t\ttop  = box.top  + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop,\n\t\t\tleft = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;\n\n\t\treturn { top: top, left: left };\n\t};\n\n} else {\n\tjQuery.fn.offset = function( options ) {\n\t\tvar elem = this[0];\n\n\t\tif ( options ) { \n\t\t\treturn this.each(function( i ) {\n\t\t\t\tjQuery.offset.setOffset( this, options, i );\n\t\t\t});\n\t\t}\n\n\t\tif ( !elem || !elem.ownerDocument ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif ( elem === elem.ownerDocument.body ) {\n\t\t\treturn jQuery.offset.bodyOffset( elem );\n\t\t}\n\n\t\tjQuery.offset.initialize();\n\n\t\tvar offsetParent = elem.offsetParent, prevOffsetParent = elem,\n\t\t\tdoc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,\n\t\t\tbody = doc.body, defaultView = doc.defaultView,\n\t\t\tprevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,\n\t\t\ttop = elem.offsetTop, left = elem.offsetLeft;\n\n\t\twhile ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {\n\t\t\tif ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === \"fixed\" ) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcomputedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;\n\t\t\ttop  -= elem.scrollTop;\n\t\t\tleft -= elem.scrollLeft;\n\n\t\t\tif ( elem === offsetParent ) {\n\t\t\t\ttop  += elem.offsetTop;\n\t\t\t\tleft += elem.offsetLeft;\n\n\t\t\t\tif ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {\n\t\t\t\t\ttop  += parseFloat( computedStyle.borderTopWidth  ) || 0;\n\t\t\t\t\tleft += parseFloat( computedStyle.borderLeftWidth ) || 0;\n\t\t\t\t}\n\n\t\t\t\tprevOffsetParent = offsetParent, offsetParent = elem.offsetParent;\n\t\t\t}\n\n\t\t\tif ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== \"visible\" ) {\n\t\t\t\ttop  += parseFloat( computedStyle.borderTopWidth  ) || 0;\n\t\t\t\tleft += parseFloat( computedStyle.borderLeftWidth ) || 0;\n\t\t\t}\n\n\t\t\tprevComputedStyle = computedStyle;\n\t\t}\n\n\t\tif ( prevComputedStyle.position === \"relative\" || prevComputedStyle.position === \"static\" ) {\n\t\t\ttop  += body.offsetTop;\n\t\t\tleft += body.offsetLeft;\n\t\t}\n\n\t\tif ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === \"fixed\" ) {\n\t\t\ttop  += Math.max( docElem.scrollTop, body.scrollTop );\n\t\t\tleft += Math.max( docElem.scrollLeft, body.scrollLeft );\n\t\t}\n\n\t\treturn { top: top, left: left };\n\t};\n}\n\njQuery.offset = {\n\tinitialize: function() {\n\t\tvar body = document.body, container = document.createElement(\"div\"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, \"marginTop\", true) ) || 0,\n\t\t\thtml = \"<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>\";\n\n\t\tjQuery.extend( container.style, { position: \"absolute\", top: 0, left: 0, margin: 0, border: 0, width: \"1px\", height: \"1px\", visibility: \"hidden\" } );\n\n\t\tcontainer.innerHTML = html;\n\t\tbody.insertBefore( container, body.firstChild );\n\t\tinnerDiv = container.firstChild;\n\t\tcheckDiv = innerDiv.firstChild;\n\t\ttd = innerDiv.nextSibling.firstChild.firstChild;\n\n\t\tthis.doesNotAddBorder = (checkDiv.offsetTop !== 5);\n\t\tthis.doesAddBorderForTableAndCells = (td.offsetTop === 5);\n\n\t\tcheckDiv.style.position = \"fixed\", checkDiv.style.top = \"20px\";\n\t\t// safari subtracts parent border width here which is 5px\n\t\tthis.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);\n\t\tcheckDiv.style.position = checkDiv.style.top = \"\";\n\n\t\tinnerDiv.style.overflow = \"hidden\", innerDiv.style.position = \"relative\";\n\t\tthis.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);\n\n\t\tthis.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);\n\n\t\tbody.removeChild( container );\n\t\tbody = container = innerDiv = checkDiv = table = td = null;\n\t\tjQuery.offset.initialize = jQuery.noop;\n\t},\n\n\tbodyOffset: function( body ) {\n\t\tvar top = body.offsetTop, left = body.offsetLeft;\n\n\t\tjQuery.offset.initialize();\n\n\t\tif ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {\n\t\t\ttop  += parseFloat( jQuery.curCSS(body, \"marginTop\",  true) ) || 0;\n\t\t\tleft += parseFloat( jQuery.curCSS(body, \"marginLeft\", true) ) || 0;\n\t\t}\n\n\t\treturn { top: top, left: left };\n\t},\n\t\n\tsetOffset: function( elem, options, i ) {\n\t\t// set position first, in-case top/left are set even on static elem\n\t\tif ( /static/.test( jQuery.curCSS( elem, \"position\" ) ) ) {\n\t\t\telem.style.position = \"relative\";\n\t\t}\n\t\tvar curElem   = jQuery( elem ),\n\t\t\tcurOffset = curElem.offset(),\n\t\t\tcurTop    = parseInt( jQuery.curCSS( elem, \"top\",  true ), 10 ) || 0,\n\t\t\tcurLeft   = parseInt( jQuery.curCSS( elem, \"left\", true ), 10 ) || 0;\n\n\t\tif ( jQuery.isFunction( options ) ) {\n\t\t\toptions = options.call( elem, i, curOffset );\n\t\t}\n\n\t\tvar props = {\n\t\t\ttop:  (options.top  - curOffset.top)  + curTop,\n\t\t\tleft: (options.left - curOffset.left) + curLeft\n\t\t};\n\t\t\n\t\tif ( \"using\" in options ) {\n\t\t\toptions.using.call( elem, props );\n\t\t} else {\n\t\t\tcurElem.css( props );\n\t\t}\n\t}\n};\n\n\njQuery.fn.extend({\n\tposition: function() {\n\t\tif ( !this[0] ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tvar elem = this[0],\n\n\t\t// Get *real* offsetParent\n\t\toffsetParent = this.offsetParent(),\n\n\t\t// Get correct offsets\n\t\toffset       = this.offset(),\n\t\tparentOffset = /^body|html$/i.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();\n\n\t\t// Subtract element margins\n\t\t// note: when an element has margin: auto the offsetLeft and marginLeft\n\t\t// are the same in Safari causing offset.left to incorrectly be 0\n\t\toffset.top  -= parseFloat( jQuery.curCSS(elem, \"marginTop\",  true) ) || 0;\n\t\toffset.left -= parseFloat( jQuery.curCSS(elem, \"marginLeft\", true) ) || 0;\n\n\t\t// Add offsetParent borders\n\t\tparentOffset.top  += parseFloat( jQuery.curCSS(offsetParent[0], \"borderTopWidth\",  true) ) || 0;\n\t\tparentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], \"borderLeftWidth\", true) ) || 0;\n\n\t\t// Subtract the two offsets\n\t\treturn {\n\t\t\ttop:  offset.top  - parentOffset.top,\n\t\t\tleft: offset.left - parentOffset.left\n\t\t};\n\t},\n\n\toffsetParent: function() {\n\t\treturn this.map(function() {\n\t\t\tvar offsetParent = this.offsetParent || document.body;\n\t\t\twhile ( offsetParent && (!/^body|html$/i.test(offsetParent.nodeName) && jQuery.css(offsetParent, \"position\") === \"static\") ) {\n\t\t\t\toffsetParent = offsetParent.offsetParent;\n\t\t\t}\n\t\t\treturn offsetParent;\n\t\t});\n\t}\n});\n\n\n// Create scrollLeft and scrollTop methods\njQuery.each( [\"Left\", \"Top\"], function( i, name ) {\n\tvar method = \"scroll\" + name;\n\n\tjQuery.fn[ method ] = function(val) {\n\t\tvar elem = this[0], win;\n\t\t\n\t\tif ( !elem ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif ( val !== undefined ) {\n\t\t\t// Set the scroll offset\n\t\t\treturn this.each(function() {\n\t\t\t\twin = getWindow( this );\n\n\t\t\t\tif ( win ) {\n\t\t\t\t\twin.scrollTo(\n\t\t\t\t\t\t!i ? val : jQuery(win).scrollLeft(),\n\t\t\t\t\t\t i ? val : jQuery(win).scrollTop()\n\t\t\t\t\t);\n\n\t\t\t\t} else {\n\t\t\t\t\tthis[ method ] = val;\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\twin = getWindow( elem );\n\n\t\t\t// Return the scroll offset\n\t\t\treturn win ? (\"pageXOffset\" in win) ? win[ i ? \"pageYOffset\" : \"pageXOffset\" ] :\n\t\t\t\tjQuery.support.boxModel && win.document.documentElement[ method ] ||\n\t\t\t\t\twin.document.body[ method ] :\n\t\t\t\telem[ method ];\n\t\t}\n\t};\n});\n\nfunction getWindow( elem ) {\n\treturn (\"scrollTo\" in elem && elem.document) ?\n\t\telem :\n\t\telem.nodeType === 9 ?\n\t\t\telem.defaultView || elem.parentWindow :\n\t\t\tfalse;\n}\n// Create innerHeight, innerWidth, outerHeight and outerWidth methods\njQuery.each([ \"Height\", \"Width\" ], function( i, name ) {\n\n\tvar type = name.toLowerCase();\n\n\t// innerHeight and innerWidth\n\tjQuery.fn[\"inner\" + name] = function() {\n\t\treturn this[0] ?\n\t\t\tjQuery.css( this[0], type, false, \"padding\" ) :\n\t\t\tnull;\n\t};\n\n\t// outerHeight and outerWidth\n\tjQuery.fn[\"outer\" + name] = function( margin ) {\n\t\treturn this[0] ?\n\t\t\tjQuery.css( this[0], type, false, margin ? \"margin\" : \"border\" ) :\n\t\t\tnull;\n\t};\n\n\tjQuery.fn[ type ] = function( size ) {\n\t\t// Get window width or height\n\t\tvar elem = this[0];\n\t\tif ( !elem ) {\n\t\t\treturn size == null ? null : this;\n\t\t}\n\t\t\n\t\tif ( jQuery.isFunction( size ) ) {\n\t\t\treturn this.each(function( i ) {\n\t\t\t\tvar self = jQuery( this );\n\t\t\t\tself[ type ]( size.call( this, i, self[ type ]() ) );\n\t\t\t});\n\t\t}\n\n\t\treturn (\"scrollTo\" in elem && elem.document) ? // does it walk and quack like a window?\n\t\t\t// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode\n\t\t\telem.document.compatMode === \"CSS1Compat\" && elem.document.documentElement[ \"client\" + name ] ||\n\t\t\telem.document.body[ \"client\" + name ] :\n\n\t\t\t// Get document width or height\n\t\t\t(elem.nodeType === 9) ? // is it a document\n\t\t\t\t// Either scroll[Width/Height] or offset[Width/Height], whichever is greater\n\t\t\t\tMath.max(\n\t\t\t\t\telem.documentElement[\"client\" + name],\n\t\t\t\t\telem.body[\"scroll\" + name], elem.documentElement[\"scroll\" + name],\n\t\t\t\t\telem.body[\"offset\" + name], elem.documentElement[\"offset\" + name]\n\t\t\t\t) :\n\n\t\t\t\t// Get or set width or height on the element\n\t\t\t\tsize === undefined ?\n\t\t\t\t\t// Get width or height on the element\n\t\t\t\t\tjQuery.css( elem, type ) :\n\n\t\t\t\t\t// Set the width or height on the element (default to pixels if value is unitless)\n\t\t\t\t\tthis.css( type, typeof size === \"string\" ? size : size + \"px\" );\n\t};\n\n});\n// Expose jQuery to the global object\nwindow.jQuery = window.$ = jQuery;\n\n})(window);\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/prepopulate.js",
    "content": "(function($) {\n    $.fn.prepopulate = function(dependencies, maxLength) {\n        /*\n            Depends on urlify.js\n            Populates a selected field with the values of the dependent fields,\n            URLifies and shortens the string. \n            dependencies - array of dependent fields id's \n            maxLength - maximum length of the URLify'd string \n        */\n        return this.each(function() {\n            var field = $(this);\n\n            field.data('_changed', false);\n            field.change(function() {\n                field.data('_changed', true);\n            });\n\n            var populate = function () {\n                // Bail if the fields value has changed\n                if (field.data('_changed') == true) return;\n \n                var values = [];\n                $.each(dependencies, function(i, field) {\n                  if ($(field).val().length > 0) {\n                      values.push($(field).val());\n                  }\n                })\n                field.val(URLify(values.join(' '), maxLength));\n            };\n\n            $(dependencies.join(',')).keyup(populate).change(populate).focus(populate);\n        });\n    };\n})(django.jQuery);\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/timeparse.js",
    "content": "var timeParsePatterns = [\n    // 9\n    {   re: /^\\d{1,2}$/i,\n        handler: function(bits) {\n            if (bits[0].length == 1) {\n                return '0' + bits[0] + ':00';\n            } else {\n                return bits[0] + ':00';\n            }\n        }\n    },\n    // 13:00\n    {   re: /^\\d{2}[:.]\\d{2}$/i,\n        handler: function(bits) {\n            return bits[0].replace('.', ':');\n        }\n    },\n    // 9:00\n    {   re: /^\\d[:.]\\d{2}$/i,\n        handler: function(bits) {\n            return '0' + bits[0].replace('.', ':');\n        }\n    },\n    // 3 am / 3 a.m. / 3am\n    {   re: /^(\\d+)\\s*([ap])(?:.?m.?)?$/i,\n        handler: function(bits) {\n            var hour = parseInt(bits[1]);\n            if (hour == 12) {\n                hour = 0;\n            }\n            if (bits[2].toLowerCase() == 'p') {\n                if (hour == 12) {\n                    hour = 0;\n                }\n                return (hour + 12) + ':00';\n            } else {\n                if (hour < 10) {\n                    return '0' + hour + ':00';\n                } else {\n                    return hour + ':00';\n                }\n            }\n        }\n    },\n    // 3.30 am / 3:15 a.m. / 3.00am\n    {   re: /^(\\d+)[.:](\\d{2})\\s*([ap]).?m.?$/i,\n        handler: function(bits) {\n            var hour = parseInt(bits[1]);\n            var mins = parseInt(bits[2]);\n            if (mins < 10) {\n                mins = '0' + mins;\n            }\n            if (hour == 12) {\n                hour = 0;\n            }\n            if (bits[3].toLowerCase() == 'p') {\n                if (hour == 12) {\n                    hour = 0;\n                }\n                return (hour + 12) + ':' + mins;\n            } else {\n                if (hour < 10) {\n                    return '0' + hour + ':' + mins;\n                } else {\n                    return hour + ':' + mins;\n                }\n            }\n        }\n    },\n    // noon\n    {   re: /^no/i,\n        handler: function(bits) {\n            return '12:00';\n        }\n    },\n    // midnight\n    {   re: /^mid/i,\n        handler: function(bits) {\n            return '00:00';\n        }\n    }\n];\n\nfunction parseTimeString(s) {\n    for (var i = 0; i < timeParsePatterns.length; i++) {\n        var re = timeParsePatterns[i].re;\n        var handler = timeParsePatterns[i].handler;\n        var bits = re.exec(s);\n        if (bits) {\n            return handler(bits);\n        }\n    }\n    return s;\n}\n"
  },
  {
    "path": "examples/django/1.2.7/static/js/urlify.js",
    "content": "var LATIN_MAP = {\n    'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç':\n    'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I',\n    'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö':\n    'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U',\n    'Ý': 'Y', 'Þ': 'TH', 'ß': 'ss', 'à':'a', 'á':'a', 'â': 'a', 'ã': 'a', 'ä':\n    'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e',\n    'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó':\n    'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u',\n    'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y'\n}\nvar LATIN_SYMBOLS_MAP = {\n    '©':'(c)'\n}\nvar GREEK_MAP = {\n    'α':'a', 'β':'b', 'γ':'g', 'δ':'d', 'ε':'e', 'ζ':'z', 'η':'h', 'θ':'8',\n    'ι':'i', 'κ':'k', 'λ':'l', 'μ':'m', 'ν':'n', 'ξ':'3', 'ο':'o', 'π':'p',\n    'ρ':'r', 'σ':'s', 'τ':'t', 'υ':'y', 'φ':'f', 'χ':'x', 'ψ':'ps', 'ω':'w',\n    'ά':'a', 'έ':'e', 'ί':'i', 'ό':'o', 'ύ':'y', 'ή':'h', 'ώ':'w', 'ς':'s',\n    'ϊ':'i', 'ΰ':'y', 'ϋ':'y', 'ΐ':'i',\n    'Α':'A', 'Β':'B', 'Γ':'G', 'Δ':'D', 'Ε':'E', 'Ζ':'Z', 'Η':'H', 'Θ':'8',\n    'Ι':'I', 'Κ':'K', 'Λ':'L', 'Μ':'M', 'Ν':'N', 'Ξ':'3', 'Ο':'O', 'Π':'P',\n    'Ρ':'R', 'Σ':'S', 'Τ':'T', 'Υ':'Y', 'Φ':'F', 'Χ':'X', 'Ψ':'PS', 'Ω':'W',\n    'Ά':'A', 'Έ':'E', 'Ί':'I', 'Ό':'O', 'Ύ':'Y', 'Ή':'H', 'Ώ':'W', 'Ϊ':'I',\n    'Ϋ':'Y'\n}\nvar TURKISH_MAP = {\n    'ş':'s', 'Ş':'S', 'ı':'i', 'İ':'I', 'ç':'c', 'Ç':'C', 'ü':'u', 'Ü':'U',\n    'ö':'o', 'Ö':'O', 'ğ':'g', 'Ğ':'G'\n}\nvar RUSSIAN_MAP = {\n    'а':'a', 'б':'b', 'в':'v', 'г':'g', 'д':'d', 'е':'e', 'ё':'yo', 'ж':'zh',\n    'з':'z', 'и':'i', 'й':'j', 'к':'k', 'л':'l', 'м':'m', 'н':'n', 'о':'o',\n    'п':'p', 'р':'r', 'с':'s', 'т':'t', 'у':'u', 'ф':'f', 'х':'h', 'ц':'c',\n    'ч':'ch', 'ш':'sh', 'щ':'sh', 'ъ':'', 'ы':'y', 'ь':'', 'э':'e', 'ю':'yu',\n    'я':'ya',\n    'А':'A', 'Б':'B', 'В':'V', 'Г':'G', 'Д':'D', 'Е':'E', 'Ё':'Yo', 'Ж':'Zh',\n    'З':'Z', 'И':'I', 'Й':'J', 'К':'K', 'Л':'L', 'М':'M', 'Н':'N', 'О':'O',\n    'П':'P', 'Р':'R', 'С':'S', 'Т':'T', 'У':'U', 'Ф':'F', 'Х':'H', 'Ц':'C',\n    'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu',\n    'Я':'Ya'\n}\nvar UKRAINIAN_MAP = {\n    'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g'\n}\nvar CZECH_MAP = {\n    'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u',\n    'ž':'z', 'Č':'C', 'Ď':'D', 'Ě':'E', 'Ň': 'N', 'Ř':'R', 'Š':'S', 'Ť':'T',\n    'Ů':'U', 'Ž':'Z'\n}\n\nvar POLISH_MAP = {\n    'ą':'a', 'ć':'c', 'ę':'e', 'ł':'l', 'ń':'n', 'ó':'o', 'ś':'s', 'ź':'z',\n    'ż':'z', 'Ą':'A', 'Ć':'C', 'Ę':'e', 'Ł':'L', 'Ń':'N', 'Ó':'o', 'Ś':'S',\n    'Ź':'Z', 'Ż':'Z'\n}\n\nvar LATVIAN_MAP = {\n    'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n',\n    'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'i',\n    'Ķ':'k', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'u', 'Ž':'Z'\n}\n\nvar ALL_DOWNCODE_MAPS=new Array()\nALL_DOWNCODE_MAPS[0]=LATIN_MAP\nALL_DOWNCODE_MAPS[1]=LATIN_SYMBOLS_MAP\nALL_DOWNCODE_MAPS[2]=GREEK_MAP\nALL_DOWNCODE_MAPS[3]=TURKISH_MAP\nALL_DOWNCODE_MAPS[4]=RUSSIAN_MAP\nALL_DOWNCODE_MAPS[5]=UKRAINIAN_MAP\nALL_DOWNCODE_MAPS[6]=CZECH_MAP\nALL_DOWNCODE_MAPS[7]=POLISH_MAP\nALL_DOWNCODE_MAPS[8]=LATVIAN_MAP\n\nvar Downcoder = new Object();\nDowncoder.Initialize = function()\n{\n    if (Downcoder.map) // already made\n        return ;\n    Downcoder.map ={}\n    Downcoder.chars = '' ;\n    for(var i in ALL_DOWNCODE_MAPS)\n    {\n        var lookup = ALL_DOWNCODE_MAPS[i]\n        for (var c in lookup)\n        {\n            Downcoder.map[c] = lookup[c] ;\n            Downcoder.chars += c ;\n        }\n     }\n    Downcoder.regex = new RegExp('[' + Downcoder.chars + ']|[^' + Downcoder.chars + ']+','g') ;\n}\n\ndowncode= function( slug )\n{\n    Downcoder.Initialize() ;\n    var downcoded =\"\"\n    var pieces = slug.match(Downcoder.regex);\n    if(pieces)\n    {\n        for (var i = 0 ; i < pieces.length ; i++)\n        {\n            if (pieces[i].length == 1)\n            {\n                var mapped = Downcoder.map[pieces[i]] ;\n                if (mapped != null)\n                {\n                    downcoded+=mapped;\n                    continue ;\n                }\n            }\n            downcoded+=pieces[i];\n        }\n    }\n    else\n    {\n        downcoded = slug;\n    }\n    return downcoded;\n}\n\n\nfunction URLify(s, num_chars) {\n    // changes, e.g., \"Petty theft\" to \"petty_theft\"\n    // remove all these words from the string before urlifying\n    s = downcode(s);\n    removelist = [\"a\", \"an\", \"as\", \"at\", \"before\", \"but\", \"by\", \"for\", \"from\",\n                  \"is\", \"in\", \"into\", \"like\", \"of\", \"off\", \"on\", \"onto\", \"per\",\n                  \"since\", \"than\", \"the\", \"this\", \"that\", \"to\", \"up\", \"via\",\n                  \"with\"];\n    r = new RegExp('\\\\b(' + removelist.join('|') + ')\\\\b', 'gi');\n    s = s.replace(r, '');\n    // if downcode doesn't hit, the char will be stripped here\n    s = s.replace(/[^-\\w\\s]/g, '');  // remove unneeded chars\n    s = s.replace(/^\\s+|\\s+$/g, ''); // trim leading/trailing spaces\n    s = s.replace(/[-\\s]+/g, '-');   // convert spaces to hyphens\n    s = s.toLowerCase();             // convert to lowercase\n    return s.substring(0, num_chars);// trim to first num_chars chars\n}\n\n"
  },
  {
    "path": "examples/django/1.4/README",
    "content": "django admin的用户名：root，密码：root\n"
  },
  {
    "path": "examples/django/1.4/config.yaml",
    "content": "name: pylabs\nversion: 9\n\nlibraries:\n- name: \"django\"\n  version: \"1.4\"\n\nhandlers:\n- url: /foo\n  static_dir: foo\n"
  },
  {
    "path": "examples/django/1.4/db.sql",
    "content": "-- phpMyAdmin SQL Dump\n-- version 3.3.8.1\n-- http://www.phpmyadmin.net\n--\n-- 主机: w.rdc.sae.sina.com.cn:3307\n-- 生成日期: 2012 年 10 月 31 日 15:39\n-- 服务器版本: 5.5.23\n-- PHP 版本: 5.2.9\n\nSET SQL_MODE=\"NO_AUTO_VALUE_ON_ZERO\";\n\n\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n/*!40101 SET NAMES utf8 */;\n\n--\n-- 数据库: `app_pylabs`\n--\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `auth_group`\n--\n\nCREATE TABLE IF NOT EXISTS `auth_group` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(80) NOT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `name` (`name`)\n) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;\n\n--\n-- 转存表中的数据 `auth_group`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `auth_group_permissions`\n--\n\nCREATE TABLE IF NOT EXISTS `auth_group_permissions` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `group_id` int(11) NOT NULL,\n  `permission_id` int(11) NOT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `group_id` (`group_id`,`permission_id`),\n  KEY `auth_group_permissions_425ae3c4` (`group_id`),\n  KEY `auth_group_permissions_1e014c8f` (`permission_id`)\n) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;\n\n--\n-- 转存表中的数据 `auth_group_permissions`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `auth_permission`\n--\n\nCREATE TABLE IF NOT EXISTS `auth_permission` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(50) NOT NULL,\n  `content_type_id` int(11) NOT NULL,\n  `codename` varchar(100) NOT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `content_type_id` (`content_type_id`,`codename`),\n  KEY `auth_permission_1bb8f392` (`content_type_id`)\n) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=28 ;\n\n--\n-- 转存表中的数据 `auth_permission`\n--\n\nINSERT INTO `auth_permission` (`id`, `name`, `content_type_id`, `codename`) VALUES\n(1, 'Can add permission', 1, 'add_permission'),\n(2, 'Can change permission', 1, 'change_permission'),\n(3, 'Can delete permission', 1, 'delete_permission'),\n(4, 'Can add group', 2, 'add_group'),\n(5, 'Can change group', 2, 'change_group'),\n(6, 'Can delete group', 2, 'delete_group'),\n(7, 'Can add user', 3, 'add_user'),\n(8, 'Can change user', 3, 'change_user'),\n(9, 'Can delete user', 3, 'delete_user'),\n(10, 'Can add content type', 4, 'add_contenttype'),\n(11, 'Can change content type', 4, 'change_contenttype'),\n(12, 'Can delete content type', 4, 'delete_contenttype'),\n(13, 'Can add session', 5, 'add_session'),\n(14, 'Can change session', 5, 'change_session'),\n(15, 'Can delete session', 5, 'delete_session'),\n(16, 'Can add site', 6, 'add_site'),\n(17, 'Can change site', 6, 'change_site'),\n(18, 'Can delete site', 6, 'delete_site'),\n(19, 'Can add poll', 7, 'add_poll'),\n(20, 'Can change poll', 7, 'change_poll'),\n(21, 'Can delete poll', 7, 'delete_poll'),\n(22, 'Can add choice', 8, 'add_choice'),\n(23, 'Can change choice', 8, 'change_choice'),\n(24, 'Can delete choice', 8, 'delete_choice'),\n(25, 'Can add log entry', 9, 'add_logentry'),\n(26, 'Can change log entry', 9, 'change_logentry'),\n(27, 'Can delete log entry', 9, 'delete_logentry');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `auth_user`\n--\n\nCREATE TABLE IF NOT EXISTS `auth_user` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `username` varchar(30) NOT NULL,\n  `first_name` varchar(30) NOT NULL,\n  `last_name` varchar(30) NOT NULL,\n  `email` varchar(75) NOT NULL,\n  `password` varchar(128) NOT NULL,\n  `is_staff` tinyint(1) NOT NULL,\n  `is_active` tinyint(1) NOT NULL,\n  `is_superuser` tinyint(1) NOT NULL,\n  `last_login` datetime NOT NULL,\n  `date_joined` datetime NOT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `username` (`username`)\n) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;\n\n--\n-- 转存表中的数据 `auth_user`\n--\n\nINSERT INTO `auth_user` (`id`, `username`, `first_name`, `last_name`, `email`, `password`, `is_staff`, `is_active`, `is_superuser`, `last_login`, `date_joined`) VALUES\n(1, 'root', '', '', 'sae@sina.com', 'pbkdf2_sha256$10000$24Z4xOfUCbbs$8W7HXan50vIdPiECMXk8E63mp5KkX7cxaDUx3SO8q4Y=', 1, 1, 1, '2012-10-31 07:25:52', '2012-10-30 06:06:28');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `auth_user_groups`\n--\n\nCREATE TABLE IF NOT EXISTS `auth_user_groups` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `user_id` int(11) NOT NULL,\n  `group_id` int(11) NOT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `user_id` (`user_id`,`group_id`),\n  KEY `auth_user_groups_403f60f` (`user_id`),\n  KEY `auth_user_groups_425ae3c4` (`group_id`)\n) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;\n\n--\n-- 转存表中的数据 `auth_user_groups`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `auth_user_user_permissions`\n--\n\nCREATE TABLE IF NOT EXISTS `auth_user_user_permissions` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `user_id` int(11) NOT NULL,\n  `permission_id` int(11) NOT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `user_id` (`user_id`,`permission_id`),\n  KEY `auth_user_user_permissions_403f60f` (`user_id`),\n  KEY `auth_user_user_permissions_1e014c8f` (`permission_id`)\n) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;\n\n--\n-- 转存表中的数据 `auth_user_user_permissions`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `django_admin_log`\n--\n\nCREATE TABLE IF NOT EXISTS `django_admin_log` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `action_time` datetime NOT NULL,\n  `user_id` int(11) NOT NULL,\n  `content_type_id` int(11) DEFAULT NULL,\n  `object_id` longtext,\n  `object_repr` varchar(200) NOT NULL,\n  `action_flag` smallint(5) unsigned NOT NULL,\n  `change_message` longtext NOT NULL,\n  PRIMARY KEY (`id`),\n  KEY `django_admin_log_403f60f` (`user_id`),\n  KEY `django_admin_log_1bb8f392` (`content_type_id`)\n) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7 ;\n\n--\n-- 转存表中的数据 `django_admin_log`\n--\n\nINSERT INTO `django_admin_log` (`id`, `action_time`, `user_id`, `content_type_id`, `object_id`, `object_repr`, `action_flag`, `change_message`) VALUES\n(1, '2012-10-30 06:18:15', 1, 7, '1', 'Poll object', 1, ''),\n(2, '2012-10-30 07:08:39', 1, 7, '1', 'Poll object', 3, ''),\n(3, '2012-10-30 07:10:39', 1, 7, '2', 'Poll object', 1, ''),\n(4, '2012-10-30 07:13:02', 1, 7, '3', 'Poll object', 1, ''),\n(5, '2012-10-30 08:00:07', 1, 7, '3', 'Poll object', 2, 'Changed question.'),\n(6, '2012-10-31 02:10:18', 1, 7, '4', 'Poll object', 1, '');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `django_content_type`\n--\n\nCREATE TABLE IF NOT EXISTS `django_content_type` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `name` varchar(100) NOT NULL,\n  `app_label` varchar(100) NOT NULL,\n  `model` varchar(100) NOT NULL,\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `app_label` (`app_label`,`model`)\n) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;\n\n--\n-- 转存表中的数据 `django_content_type`\n--\n\nINSERT INTO `django_content_type` (`id`, `name`, `app_label`, `model`) VALUES\n(1, 'permission', 'auth', 'permission'),\n(2, 'group', 'auth', 'group'),\n(3, 'user', 'auth', 'user'),\n(4, 'content type', 'contenttypes', 'contenttype'),\n(5, 'session', 'sessions', 'session'),\n(6, 'site', 'sites', 'site'),\n(7, 'poll', 'polls', 'poll'),\n(8, 'choice', 'polls', 'choice'),\n(9, 'log entry', 'admin', 'logentry');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `django_session`\n--\n\nCREATE TABLE IF NOT EXISTS `django_session` (\n  `session_key` varchar(40) NOT NULL,\n  `session_data` longtext NOT NULL,\n  `expire_date` datetime NOT NULL,\n  PRIMARY KEY (`session_key`),\n  KEY `django_session_3da3d3d8` (`expire_date`)\n) ENGINE=MyISAM DEFAULT CHARSET=latin1;\n\n--\n-- 转存表中的数据 `django_session`\n--\n\nINSERT INTO `django_session` (`session_key`, `session_data`, `expire_date`) VALUES\n('008ad7e03862e1629e6daae522b2c966', 'NjBkMmM1YjZiNzc3Zjk4ZjM4ZDg2NjlmNWQzMTM4ZjdiMWNmMWY4MDqAAn1xAShVEl9hdXRoX3Vz\\nZXJfYmFja2VuZHECVSlkamFuZ28uY29udHJpYi5hdXRoLmJhY2tlbmRzLk1vZGVsQmFja2VuZHED\\nVQ1fYXV0aF91c2VyX2lkcQSKAQF1Lg==\\n', '2012-11-13 06:16:18'),\n('06d2ada6d5dc0420b6c7a3065f5b1c75', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:58:46'),\n('b3c87ecbc2782d39f2d566fd13d49f9f', 'NjBkMmM1YjZiNzc3Zjk4ZjM4ZDg2NjlmNWQzMTM4ZjdiMWNmMWY4MDqAAn1xAShVEl9hdXRoX3Vz\\nZXJfYmFja2VuZHECVSlkamFuZ28uY29udHJpYi5hdXRoLmJhY2tlbmRzLk1vZGVsQmFja2VuZHED\\nVQ1fYXV0aF91c2VyX2lkcQSKAQF1Lg==\\n', '2012-11-13 07:56:32'),\n('410b45fe33d441e1967a8bea227a720f', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:58:48'),\n('314bda20732bf0bb56151571c819db61', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:58:49'),\n('454f6334a73fea0d5788b3bad6d58b8a', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:58:50'),\n('5b2f4a831f481b5fa26474a0f3e25ac5', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:58:51'),\n('87aff8f07792b206b99ebe92821700e8', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:58:51'),\n('912a49c543d76ba48650386b2c17f7b5', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:58:52'),\n('752957b0702ce16986ffadd7332caef2', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:58:53'),\n('aa9fb069f2800565f7e0998ab4e380df', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:58:54'),\n('b64e0a0614bd6fd61b6b777d6e773e98', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:58:54'),\n('5a1f7dd0a06f1a8bc211086f7e301920', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:33'),\n('ac3ad8cfe330726bc8e8e2d3def571cb', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:33'),\n('4c313aac8989ddd596999673f1a43e6f', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:34'),\n('118233d5be7f4063fcc98cad822b52af', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:35'),\n('e6e018f3fc08914fab634fff165563b6', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:35'),\n('6b5f51469ffa3156dc1829b2bf3fb49b', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:36'),\n('e429be9a654e6aeabe767b36c8528de5', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:36'),\n('0719c46deecbdacdcd7ba9859c648f3d', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:36'),\n('b9563822341436355e372e75286677dc', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:37'),\n('ee3837677cfe9bcdf7d3520f06717c53', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:37'),\n('c79e79a0a7c2ddd91eaec21120044fa4', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:37'),\n('16e0a5777a3ee20e3c11ad02760569b1', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 01:59:37'),\n('4ebbd66c3f3a5e85ac3188524fdc555c', 'ZWE2OWYzNmYwNjc2MjY0OGVmNjJjOTc4YzUwNjk0MTlmZGY5NWZjMDqAAn1xAVUKdGVzdGNvb2tp\\nZXECVQZ3b3JrZWRxA3Mu\\n', '2012-11-14 02:00:07'),\n('3a45a6128f11119ef31417ea806cb075', 'NjBkMmM1YjZiNzc3Zjk4ZjM4ZDg2NjlmNWQzMTM4ZjdiMWNmMWY4MDqAAn1xAShVEl9hdXRoX3Vz\\nZXJfYmFja2VuZHECVSlkamFuZ28uY29udHJpYi5hdXRoLmJhY2tlbmRzLk1vZGVsQmFja2VuZHED\\nVQ1fYXV0aF91c2VyX2lkcQSKAQF1Lg==\\n', '2012-11-14 07:25:52'),\n('4268b618f958a05f61931dd6eab3f098', 'gAJ9cQFVCnRlc3Rjb29raWVxAlUGd29ya2VkcQNzLjhhNjFmYjExYjZmNjY3ZmI0NWI0NjU4Y2E0\\nZGE3YzJi\\n', '2012-11-14 15:38:47'),\n('4d50416d7e4b8f7f20e242182ae8fdb8', 'gAJ9cQFVCnRlc3Rjb29raWVxAlUGd29ya2VkcQNzLjhhNjFmYjExYjZmNjY3ZmI0NWI0NjU4Y2E0\\nZGE3YzJi\\n', '2012-11-14 15:38:49'),\n('39a905326781e4eff970e7ae342407f8', 'gAJ9cQFVCnRlc3Rjb29raWVxAlUGd29ya2VkcQNzLjhhNjFmYjExYjZmNjY3ZmI0NWI0NjU4Y2E0\\nZGE3YzJi\\n', '2012-11-14 15:38:52');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `django_site`\n--\n\nCREATE TABLE IF NOT EXISTS `django_site` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `domain` varchar(100) NOT NULL,\n  `name` varchar(50) NOT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;\n\n--\n-- 转存表中的数据 `django_site`\n--\n\nINSERT INTO `django_site` (`id`, `domain`, `name`) VALUES\n(1, 'example.com', 'example.com');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `polls_choice`\n--\n\nCREATE TABLE IF NOT EXISTS `polls_choice` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `poll_id` int(11) NOT NULL,\n  `choice` varchar(200) NOT NULL,\n  `votes` int(11) NOT NULL,\n  PRIMARY KEY (`id`),\n  KEY `polls_choice_763e883` (`poll_id`)\n) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;\n\n--\n-- 转存表中的数据 `polls_choice`\n--\n\nINSERT INTO `polls_choice` (`id`, `poll_id`, `choice`, `votes`) VALUES\n(1, 2, 'Jaime', 1),\n(2, 2, 'Tryion', 3),\n(3, 2, 'Ned Stark', 3),\n(4, 3, 'yes', 3),\n(5, 3, 'no', 4),\n(6, 4, 'Stark', 2),\n(7, 4, 'Targaren', 1),\n(8, 4, 'Lannister', 1);\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `polls_poll`\n--\n\nCREATE TABLE IF NOT EXISTS `polls_poll` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `question` varchar(200) NOT NULL,\n  `pub_date` datetime NOT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;\n\n--\n-- 转存表中的数据 `polls_poll`\n--\n\nINSERT INTO `polls_poll` (`id`, `question`, `pub_date`) VALUES\n(2, 'Which character do you like best?', '2012-10-30 20:10:26'),\n(3, 'Do you think the queue Cercei is totally a nuts?', '2012-10-30 20:12:58'),\n(4, 'Which house do you think will win the Iron Throne in the end?', '2012-10-31 02:10:44');\n"
  },
  {
    "path": "examples/django/1.4/index.wsgi",
    "content": "import sae\nfrom mysite import wsgi\n\napplication = sae.create_wsgi_app(wsgi.application)\n"
  },
  {
    "path": "examples/django/1.4/manage.py",
    "content": "#!/usr/bin/env python\nimport os\nimport sys\n\nif __name__ == \"__main__\":\n    os.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"mysite.settings\")\n\n    from django.core.management import execute_from_command_line\n\n    execute_from_command_line(sys.argv)\n"
  },
  {
    "path": "examples/django/1.4/mysite/__init__.py",
    "content": ""
  },
  {
    "path": "examples/django/1.4/mysite/settings.py",
    "content": "# Django settings for mysite project.\n\nDEBUG = True\nTEMPLATE_DEBUG = DEBUG\n\nADMINS = (\n    # ('Your Name', 'your_email@example.com'),\n)\n\nMANAGERS = ADMINS\n\nimport os\n\nif 'SERVER_SOFTWARE' in os.environ:\n    from sae.const import (\n        MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASS, MYSQL_DB\n    )\nelse:\n    # Make `python manage.py syncdb` works happy!\n    MYSQL_HOST = 'localhost'\n    MYSQL_PORT = '3306'\n    MYSQL_USER = 'root'\n    MYSQL_PASS = 'root'\n    MYSQL_DB   = 'app_pylabs'\n\nDATABASES = {\n    'default': {\n        'ENGINE':   'django.db.backends.mysql',\n        'NAME':     MYSQL_DB,\n        'USER':     MYSQL_USER,\n        'PASSWORD': MYSQL_PASS,\n        'HOST':     MYSQL_HOST,\n        'PORT':     MYSQL_PORT,\n    }\n}\n\n# Local time zone for this installation. Choices can be found here:\n# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name\n# although not all choices may be available on all operating systems.\n# On Unix systems, a value of None will cause Django to use the same\n# timezone as the operating system.\n# If running in a Windows environment this must be set to the same as your\n# system time zone.\nTIME_ZONE = 'America/Chicago'\n\n# Language code for this installation. All choices can be found here:\n# http://www.i18nguy.com/unicode/language-identifiers.html\nLANGUAGE_CODE = 'en-us'\n\nSITE_ID = 1\n\n# If you set this to False, Django will make some optimizations so as not\n# to load the internationalization machinery.\nUSE_I18N = True\n\n# If you set this to False, Django will not format dates, numbers and\n# calendars according to the current locale.\nUSE_L10N = True\n\n# If you set this to False, Django will not use timezone-aware datetimes.\nUSE_TZ = True\n\n# Absolute filesystem path to the directory that will hold user-uploaded files.\n# Example: \"/home/media/media.lawrence.com/media/\"\nMEDIA_ROOT = ''\n\n# URL that handles the media served from MEDIA_ROOT. Make sure to use a\n# trailing slash.\n# Examples: \"http://media.lawrence.com/media/\", \"http://example.com/media/\"\nMEDIA_URL = ''\n\n# Absolute path to the directory static files should be collected to.\n# Don't put anything in this directory yourself; store your static files\n# in apps' \"static/\" subdirectories and in STATICFILES_DIRS.\n# Example: \"/home/media/media.lawrence.com/static/\"\nSTATIC_ROOT = ''\n\n# URL prefix for static files.\n# Example: \"http://media.lawrence.com/static/\"\nSTATIC_URL = '/static/'\n\n# Additional locations of static files\nSTATICFILES_DIRS = (\n    # Put strings here, like \"/home/html/static\" or \"C:/www/django/static\".\n    # Always use forward slashes, even on Windows.\n    # Don't forget to use absolute paths, not relative paths.\n)\n\n# List of finder classes that know how to find static files in\n# various locations.\nSTATICFILES_FINDERS = (\n    'django.contrib.staticfiles.finders.FileSystemFinder',\n    'django.contrib.staticfiles.finders.AppDirectoriesFinder',\n#    'django.contrib.staticfiles.finders.DefaultStorageFinder',\n)\n\n# Make this unique, and don't share it with anybody.\nSECRET_KEY = 'up)24f2-l-+#g7ek4hp8ri1ng$@nbwqk+(fhdshgn9sc#b*oyl'\n\n# List of callables that know how to import templates from various sources.\nTEMPLATE_LOADERS = (\n    'django.template.loaders.filesystem.Loader',\n    'django.template.loaders.app_directories.Loader',\n#     'django.template.loaders.eggs.Loader',\n)\n\nMIDDLEWARE_CLASSES = (\n    'django.middleware.common.CommonMiddleware',\n    'django.contrib.sessions.middleware.SessionMiddleware',\n    'django.middleware.csrf.CsrfViewMiddleware',\n    'django.contrib.auth.middleware.AuthenticationMiddleware',\n    'django.contrib.messages.middleware.MessageMiddleware',\n    # Uncomment the next line for simple clickjacking protection:\n    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',\n)\n\nROOT_URLCONF = 'mysite.urls'\n\n# Python dotted path to the WSGI application used by Django's runserver.\nWSGI_APPLICATION = 'mysite.wsgi.application'\n\nimport os.path\n\nTEMPLATE_DIRS = (\n    # Put strings here, like \"/home/html/django_templates\" or \"C:/www/django/templates\".\n    # Always use forward slashes, even on Windows.\n    # Don't forget to use absolute paths, not relative paths.\n    os.path.join(os.path.dirname(__file__), 'templates'),\n)\n\nINSTALLED_APPS = (\n    'django.contrib.auth',\n    'django.contrib.contenttypes',\n    'django.contrib.sessions',\n    'django.contrib.sites',\n    'django.contrib.messages',\n    'django.contrib.staticfiles',\n    # Uncomment the next line to enable the admin:\n    'django.contrib.admin',\n    # Uncomment the next line to enable admin documentation:\n    # 'django.contrib.admindocs',\n    'polls',\n)\n\n# A sample logging configuration. The only tangible logging\n# performed by this configuration is to send an email to\n# the site admins on every HTTP 500 error when DEBUG=False.\n# See http://docs.djangoproject.com/en/dev/topics/logging for\n# more details on how to customize your logging configuration.\nLOGGING = {\n    'version': 1,\n    'disable_existing_loggers': False,\n    'filters': {\n        'require_debug_false': {\n            '()': 'django.utils.log.RequireDebugFalse'\n        }\n    },\n    'handlers': {\n        'mail_admins': {\n            'level': 'ERROR',\n            'filters': ['require_debug_false'],\n            'class': 'django.utils.log.AdminEmailHandler'\n        }\n    },\n    'loggers': {\n        'django.request': {\n            'handlers': ['mail_admins'],\n            'level': 'ERROR',\n            'propagate': True,\n        },\n    }\n}\n"
  },
  {
    "path": "examples/django/1.4/mysite/templates/404.html",
    "content": "<h1>NOT FOUND</h1>\n"
  },
  {
    "path": "examples/django/1.4/mysite/templates/500.html",
    "content": "<h1>INTERNAL ERROR</h1>\n"
  },
  {
    "path": "examples/django/1.4/mysite/urls.py",
    "content": "from django.conf.urls import patterns, include, url\n\n# Uncomment the next two lines to enable the admin:\nfrom django.contrib import admin\nadmin.autodiscover()\n\nurlpatterns = patterns('',\n    # Examples:\n    # url(r'^$', 'mysite.views.home', name='home'),\n    # url(r'^mysite/', include('mysite.foo.urls')),\n    url(r'^polls/', include('polls.urls')),\n\n    # Uncomment the admin/doc line below to enable admin documentation:\n    # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),\n\n    # Uncomment the next line to enable the admin:\n    url(r'^admin/', include(admin.site.urls)),\n)\n\n# Serve static files for admin, use this for debug usage only\n# `python manage.py collectstatic` is preferred.\nfrom django.contrib.staticfiles.urls import staticfiles_urlpatterns\nurlpatterns += staticfiles_urlpatterns()\n\n"
  },
  {
    "path": "examples/django/1.4/mysite/wsgi.py",
    "content": "\"\"\"\nWSGI config for mysite project.\n\nThis module contains the WSGI application used by Django's development server\nand any production WSGI deployments. It should expose a module-level variable\nnamed ``application``. Django's ``runserver`` and ``runfcgi`` commands discover\nthis application via the ``WSGI_APPLICATION`` setting.\n\nUsually you will have the standard Django WSGI application here, but it also\nmight make sense to replace the whole Django WSGI application with a custom one\nthat later delegates to the Django one. For example, you could introduce WSGI\nmiddleware here, or combine a Django application with an application of another\nframework.\n\n\"\"\"\nimport os\n\nos.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"mysite.settings\")\n\n# This application object is used by any WSGI server configured to use this\n# file. This includes Django's development server, if the WSGI_APPLICATION\n# setting points here.\nfrom django.core.wsgi import get_wsgi_application\napplication = get_wsgi_application()\n\n# Apply WSGI middleware here.\n# from helloworld.wsgi import HelloWorldApplication\n# application = HelloWorldApplication(application)\n"
  },
  {
    "path": "examples/django/1.4/polls/__init__.py",
    "content": ""
  },
  {
    "path": "examples/django/1.4/polls/admin.py",
    "content": "from django.contrib import admin\nfrom polls.models import Poll, Choice\n\nclass ChoiceInline(admin.StackedInline):\n    model = Choice\n    extra = 3\n\nclass PollAdmin(admin.ModelAdmin):\n    fieldsets = [\n        (None,               {'fields': ['question']}),\n        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),\n    ]\n    inlines = [ChoiceInline]\n    list_display = ('question', 'pub_date', 'was_published_recently')\n    list_filter = ['pub_date']\n    search_fields = ['question']\n    date_hierarchy = 'pub_date'\n\nadmin.site.register(Poll, PollAdmin)\n"
  },
  {
    "path": "examples/django/1.4/polls/models.py",
    "content": "import time\nfrom django.db import models\n\nclass Poll(models.Model):\n    question = models.CharField(max_length=200)\n    pub_date = models.DateTimeField('date published')\n\n    def was_published_recently(self):\n        return self.pub_date >= time.now() - datetime.timedelta(days=1)\n\n    was_published_recently.admin_order_field = 'pub_date'\n    was_published_recently.boolean = True\n    was_published_recently.short_description = 'Published recently?'\n\nclass Choice(models.Model):\n    poll = models.ForeignKey(Poll)\n    choice = models.CharField(max_length=200)\n    votes = models.IntegerField()\n"
  },
  {
    "path": "examples/django/1.4/polls/templates/detail.html",
    "content": "<h1>{{ poll.question }}</h1>\n\n{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}\n\n<form action=\"/polls/{{ poll.id }}/vote/\" method=\"post\">\n{% csrf_token %}\n{% for choice in poll.choice_set.all %}\n    <input type=\"radio\" name=\"choice\" id=\"choice{{ forloop.counter }}\" value=\"{{ choice.id }}\" />\n    <label for=\"choice{{ forloop.counter }}\">{{ choice.choice }}</label><br />\n{% endfor %}\n<input type=\"submit\" value=\"Vote\" />\n</form>\n"
  },
  {
    "path": "examples/django/1.4/polls/templates/index.html",
    "content": "{% if latest_poll_list %}\n    <ul>\n    {% for poll in latest_poll_list %}\n        <li><a href=\"/polls/{{ poll.id }}/\">{{ poll.question }}</a></li>\n    {% endfor %}\n    </ul>\n{% else %}\n    <p>No polls are available.</p>\n{% endif %}\n"
  },
  {
    "path": "examples/django/1.4/polls/templates/results.html",
    "content": "<h1>{{ poll.question }}</h1>\n\n<ul>\n{% for choice in poll.choice_set.all %}\n    <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>\n{% endfor %}\n</ul>\n\n<a href=\"/polls/{{ poll.id }}/\">Vote again?</a>\n"
  },
  {
    "path": "examples/django/1.4/polls/tests.py",
    "content": "\"\"\"\nThis file demonstrates writing tests using the unittest module. These will pass\nwhen you run \"manage.py test\".\n\nReplace this with more appropriate tests for your application.\n\"\"\"\n\nfrom django.test import TestCase\n\n\nclass SimpleTest(TestCase):\n    def test_basic_addition(self):\n        \"\"\"\n        Tests that 1 + 1 always equals 2.\n        \"\"\"\n        self.assertEqual(1 + 1, 2)\n"
  },
  {
    "path": "examples/django/1.4/polls/urls.py",
    "content": "from django.conf.urls import patterns, include, url\n\nurlpatterns = patterns('polls.views',\n    url(r'^$', 'index'),\n    url(r'^(?P<poll_id>\\d+)/$', 'detail'),\n    url(r'^(?P<poll_id>\\d+)/results/$', 'results'),\n    url(r'^(?P<poll_id>\\d+)/vote/$', 'vote'),\n)\n"
  },
  {
    "path": "examples/django/1.4/polls/views.py",
    "content": "from django.shortcuts import render_to_response, get_object_or_404\nfrom django.template import RequestContext\nfrom django.http import HttpResponseRedirect, HttpResponse\nfrom django.core.urlresolvers import reverse\nfrom polls.models import Poll, Choice\n\ndef index(request):\n    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]\n    return render_to_response('index.html', {'latest_poll_list': latest_poll_list})\n\ndef detail(request, poll_id):\n    p = get_object_or_404(Poll, pk=poll_id)\n    return render_to_response('detail.html', {'poll': p},\n                               context_instance=RequestContext(request))\n\ndef vote(request, poll_id):\n    p = get_object_or_404(Poll, pk=poll_id)\n    try:\n        selected_choice = p.choice_set.get(pk=request.POST['choice'])\n    except (KeyError, Choice.DoesNotExist):\n        # Redisplay the poll voting form.\n        return render_to_response('detail.html', {\n            'poll': p,\n            'error_message': \"You didn't select a choice.\",\n        }, context_instance=RequestContext(request))\n    else:\n        selected_choice.votes += 1\n        selected_choice.save()\n        # Always return an HttpResponseRedirect after successfully dealing\n        # with POST data. This prevents data from being posted twice if a\n        # user hits the Back button.\n        return HttpResponseRedirect(reverse('polls.views.results', args=(p.id,)))\n\ndef results(request, poll_id):\n    p = get_object_or_404(Poll, pk=poll_id)\n    return render_to_response('results.html', {'poll': p})\n"
  },
  {
    "path": "examples/flask/README",
    "content": "Hello, Flask!\n"
  },
  {
    "path": "examples/flask/app.py",
    "content": ""
  },
  {
    "path": "examples/flask/index.wsgi",
    "content": "\nimport sae\n\nfrom myapp import app\n\napplication = sae.create_wsgi_app(app)\n"
  },
  {
    "path": "examples/flask/myapp.py",
    "content": "\nimport MySQLdb\nfrom flask import Flask, g, request\n\napp = Flask(__name__)\napp.debug = True\n\nfrom sae.const import (MYSQL_HOST, MYSQL_HOST_S,\n    MYSQL_PORT, MYSQL_USER, MYSQL_PASS, MYSQL_DB\n)\n\n@app.before_request\ndef before_request():\n    g.db = MySQLdb.connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS,\n                           MYSQL_DB, port=int(MYSQL_PORT))\n\n@app.teardown_request\ndef teardown_request(exception):\n    if hasattr(g, 'db'): g.db.close()\n\n@app.route('/')\ndef hello():\n    return \"Hello, world! - Flask\"\n\n@app.route('/demo', methods=['GET', 'POST'])\ndef greeting():\n    html = ''\n\n    if request.method == 'POST':\n        c = g.db.cursor()\n        c.execute(\"insert into demo(text) values(%s)\", (request.form['text']))\n\n    html += \"\"\"\n    <form action=\"\" method=\"post\">\n        <div><textarea cols=\"40\" name=\"text\"></textarea></div>\n        <div><input type=\"submit\" /></div>\n    </form>\n    \"\"\"\n    c = g.db.cursor()\n    c.execute('select * from demo')\n    msgs = list(c.fetchall())\n    msgs.reverse()\n    for row in msgs:\n        html +=  '<p>' + row[-1] + '</p>'\n\n    return html\n\n"
  },
  {
    "path": "examples/helloworld/1/index.wsgi",
    "content": "import sae\n\ndef app(environ, start_response):\n    status = '200 OK'\n    response_headers = [('Content-type', 'text/plain')]\n    start_response(status, response_headers)\n    return ['Hello, world!']\n\napplication = sae.create_wsgi_app(app)\n"
  },
  {
    "path": "examples/matplotshell/README",
    "content": "Run matplotlib source code on SAE.\n\nhttp://matplotlib.org/gallery.html\n"
  },
  {
    "path": "examples/matplotshell/config.yaml",
    "content": "name: matplotshell\nversion: 1\n\nlibraries:\n- name: numpy\n  version: \"1.6.1\"\n    \n- name: matplotlib\n  version: \"1.1.1\"\n"
  },
  {
    "path": "examples/matplotshell/index.wsgi",
    "content": "import sae\nimport cStringIO\nfrom bottle import Bottle, request, response\n\napp = Bottle()\n\n@app.route('/')\ndef index():\n    return \"\"\"\n<html>\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n    <title>matplotlib shell</title>\n  </head>\n  <body>\n    <form action=\"/plot\" method=\"POST\">\n      <div id=\"code\">\n        <input type=\"submit\" value=\"RUN\"/>\n        <hr>\n        <textarea rows=\"25\" cols=\"80\" name=\"code\"></textarea>\n      </div>\n      <hr/>\n    </form>\n    <div id=\"quotes\" class=\"clearfix\">\n    </div>\n  </body>\n</html>\n\"\"\"\n\n@app.route('/plot', method='POST')\ndef plot():\n    code = request.forms['code']\n\n    import matplotlib.pyplot as plt\n    #import pylab as plt\n    __f__ = cStringIO.StringIO()\n    \n    dct = dict()\n    dct['__name__'] = '__main__'\n    exec code in dct\n    plt.savefig(__f__)\n    data = __f__.getvalue()\n    __f__.close()\n\n    # close current session\n    plt.close()\n\n    response.content_type = 'image/png'\n    return data\n\nimport bottle\nbottle.debug(True)\n\napplication = sae.create_wsgi_app(app)\n"
  },
  {
    "path": "examples/renren/config.yaml",
    "content": "name: pylabs\nversion: 2\n"
  },
  {
    "path": "examples/renren/db.sql",
    "content": "-- phpMyAdmin SQL Dump\n-- version 3.3.8.1\n-- http://www.phpmyadmin.net\n--\n-- 主机: w.rdc.sae.sina.com.cn:3307\n-- 生成日期: 2012 年 03 月 14 日 00:24\n-- 服务器版本: 5.1.47\n-- PHP 版本: 5.2.9\n\nSET SQL_MODE=\"NO_AUTO_VALUE_ON_ZERO\";\n\n--\n-- 数据库: `app_pylabs`\n--\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `users`\n--\n\nCREATE TABLE IF NOT EXISTS `users` (\n  `uid` int(11) NOT NULL,\n  `name` varchar(128) NOT NULL,\n  `avatar` varchar(128) NOT NULL,\n  `access_token` varchar(256) NOT NULL,\n  UNIQUE KEY `uid` (`uid`)\n) ENGINE=MyISAM DEFAULT CHARSET=utf8;\n\n"
  },
  {
    "path": "examples/renren/error.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n    <title>RenRen OAuth Example</title>\n  </head>\n  <body>\n  \t  <h4>{{ error.error }}</h4>\n\t  <p style=\"color: red\">{{ error.error_description }}</p>\n      <p>For more information please browse the <a href=\"{{ error.error_uri }}\">error page</a>.</p>\n      <p><a href=\"/auth/login\">Log in with RenRen</a></p>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/renren/index.wsgi",
    "content": "import sae\nfrom renrenoauth import app\n\napplication = sae.create_wsgi_app(app)\n"
  },
  {
    "path": "examples/renren/oauth.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n    <head>\n        <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n        <title>RenRen OAuth Example</title>\n    </head>\n    <body>\n        {% if current_user %}\n        <p>\n            <a target=\"_blank\" href=\"http://www.renren.com/profile?id={{ current_user.uid }}\"><img src=\"{{ current_user.avatar }}\" alt=\"首页\" /></a>\n        </p>\n        <p>\n            You are logged in as <a target=\"_blank\" href=\"http://www.renren.com/profile?id={{ current_user.uid }}\">{{ current_user.name }}</a>\n        </p>\n        <p>\n            <a href=\"/auth/logout\">Log out</a>\n        </p>\n        {% else %}\n        <p>\n            You are not yet logged into this site\n        </p>\n        <p>\n            <a href=\"/auth/login\">Log in with RenRen</a>\n        </p>\n        {% end %}\n    </body>\n</html>\n"
  },
  {
    "path": "examples/renren/renrenoauth.py",
    "content": "#!/usr/bin/env python\n#coding=utf-8\n# \n# Copyright 2010 RenRen\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License. You may obtain\n# a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n# License for the specific language governing permissions and limitations\n# under the License.\n\n\"\"\"A demo SAE application that uses RenRen for login.\n\nThis application is modified from the offical renren oauth sdk for python.\n\nThis application uses OAuth 2.0 directly rather than relying on renren's\nJavaScript SDK for login. It also accesses the RenRen API directly\nusing the Python SDK. It is designed to illustrate how easy\nit is to use the renren Platform without any third party code.\n\nBefor runing the demo, you have to register a RenRen Application and modify the root domain.\ne.g. If you specify the redirect_rui as \"http://www.example.com/example_uri\". The root domain must be \"example.com\"\n\n@Author 414nch4n <chenfeng2@staff.sina.com.cn>\n\n\"\"\"\n\n# Replace these keys with your own one.\nRENREN_APP_API_KEY = \"06c0673d123240e7acd75e181cb5e40c\"\nRENREN_APP_SECRET_KEY = \"a11b055a759241bd8bc6af9d99aacbd4\"\n\n\nRENREN_AUTHORIZATION_URI = \"http://graph.renren.com/oauth/authorize\"\nRENREN_ACCESS_TOKEN_URI = \"http://graph.renren.com/oauth/token\"\nRENREN_SESSION_KEY_URI = \"http://graph.renren.com/renren_api/session_key\"\nRENREN_API_SERVER = \"http://api.renren.com/restserver.do\"\n\n\n\nimport base64\nimport Cookie\nimport email.utils\nimport hashlib\nimport hmac\nimport logging\nimport os.path\nimport time\nimport urllib\n\n# Find a JSON parser\ntry:\n    import json\n    _parse_json = lambda s: json.loads(s)\nexcept ImportError:\n    try:\n        import simplejson\n        _parse_json = lambda s: simplejson.loads(s)\n    except ImportError:\n        from django.utils import simplejson\n        _parse_json = lambda s: simplejson.loads(s)\n\nimport tornado.web\nimport tornado.wsgi\nimport tornado.database\n\nfrom sae.const import (MYSQL_HOST, MYSQL_HOST_S,\n    MYSQL_PORT, MYSQL_USER, MYSQL_PASS, MYSQL_DB\n)\n\n_db = tornado.database.Connection(\n   ':'.join([MYSQL_HOST, MYSQL_PORT]), MYSQL_DB, MYSQL_USER, MYSQL_PASS,\n   max_idle_time = 5\n)\n\nclass User:\n    def __init__(self, uid=None, name=None, avatar=None, access_token=None):\n        self.uid = uid\n        self.name = name\n        self.avatar = avatar\n        self.access_token = access_token\n\n    @classmethod\n    def get(cls, uid):\n        user = cls()\n        row = _db.get(\"\"\"\n            select * from users where uid = %s\n        \"\"\", uid)\n        user.uid = row.uid\n        user.name = row.name\n        user.avatar = row.avatar\n        user.access_token = row.access_token\n        return user\n\n    def put(self):\n        _db.execute(\"\"\"\n            insert into users(uid, name, avatar, access_token)\n            values(%s, %s, %s, %s) on duplicate key update\n            name = %s, avatar = %s, access_token = %s\n        \"\"\", self.uid, self.name, self.avatar, self.access_token,\n        self.name, self.avatar, self.access_token)\n\nclass BaseHandler(tornado.web.RequestHandler):\n    @property\n    def current_user(self):\n        \"\"\"Returns the logged in renren user, or None if unconnected.\"\"\"\n        if not hasattr(self, \"_current_user\"):\n            self._current_user = None\n            user_id = parse_cookie(self.get_secure_cookie(\"renren_user\"))\n            if user_id:\n                logging.info(\"renren_user in cookie is: %s\", user_id)\n                self._current_user = User.get(user_id)\n        return self._current_user\n\nclass HomeHandler(BaseHandler):\n    def get(self):\n        template_file = os.path.join(os.path.dirname(__file__), \n                                     'oauth.html')\n        self.render(template_file, current_user=self.current_user)\n\nclass LoginHandler(BaseHandler):\n    def get(self):\n        verification_code = self.get_argument(\"code\", None)\n        # FIXME: use path_url from the request to construct the redirect_uri\n        args = dict(client_id=RENREN_APP_API_KEY, redirect_uri='http://%s/auth/login' % self.request.host)\n        \n        error = self.get_argument(\"error\", None)\n        \n        if error:\n            args[\"error\"] = error\n            args[\"error_description\"] = self.get_argument(\"error_description\", '')\n            args[\"error_uri\"] = self.get_argument(\"error_uri\", '')\n            path = os.path.join(os.path.dirname(__file__), \"error.html\")\n            args = dict(error=args)\n            self.render(path, **args)\n        elif verification_code:\n            scope = self.get_argument(\"scope\", \"\")\n            scope_array = str(scope).split(\"[\\\\s,+]\")\n            logging.info(\"returning scope is :\" + str(scope_array))\n            response_state = self.get_argument(\"state\", \"\")\n            logging.info(\"returning state is :\" + response_state)\n            args[\"client_secret\"] = RENREN_APP_SECRET_KEY\n            args[\"code\"] = verification_code\n            args[\"grant_type\"] = \"authorization_code\"\n            logging.info(RENREN_ACCESS_TOKEN_URI + \"?\" + urllib.urlencode(args))\n            response = urllib.urlopen(RENREN_ACCESS_TOKEN_URI + \"?\" + urllib.urlencode(args)).read()\n            logging.info(response)\n            access_token = _parse_json(response)[\"access_token\"]\n            logging.info(\"obtained access_token is: \" + access_token)\n            \n            '''Obtain session key from the Resource Service.'''\n            session_key_request_args = {\"oauth_token\": access_token}\n            response = urllib.urlopen(RENREN_SESSION_KEY_URI + \"?\" + urllib.urlencode(session_key_request_args)).read()\n            logging.info(\"session_key service response: \" + str(response))\n            session_key = str(_parse_json(response)[\"renren_token\"][\"session_key\"])\n            logging.info(\"obtained session_key is: \" + session_key)\n            \n            '''Requesting the Renren API Server obtain the user's base info.'''\n            params = {\"method\": \"users.getInfo\", \"fields\": \"name,tinyurl\"}\n            api_client = RenRenAPIClient(session_key, RENREN_APP_API_KEY, RENREN_APP_SECRET_KEY)\n            response = api_client.request(params);\n            \n            if type(response) is list:\n                response = response[0]\n            \n            user_id = response[\"uid\"]#str(access_token).split(\"-\")[1]\n            name = response[\"name\"]\n            avatar = response[\"tinyurl\"]\n            \n            user = User(uid=user_id, name=name, avatar=avatar, access_token=access_token)\n            user.put()\n            \n            set_cookie(self, \"renren_user\", str(user_id),\n                       expires=time.time() + 30 * 86400)\n            self.redirect(\"/\")\n        else:\n            args[\"response_type\"] = \"code\"\n            args[\"scope\"] = \"publish_feed email status_update\"\n            args[\"state\"] = \"1 23 abc&?|.\"\n            self.redirect(\n                RENREN_AUTHORIZATION_URI + \"?\" +\n                urllib.urlencode(args))\n\n\nclass LogoutHandler(BaseHandler):\n    def get(self):\n        self.clear_cookie('renren_user')\n        self.redirect(\"/\")\n\nclass RenRenAPIClient(object):\n    def __init__(self, session_key = None, api_key = None, secret_key = None):\n        self.session_key = session_key\n        self.api_key = api_key\n        self.secret_key = secret_key\n    def request(self, params = None):\n        \"\"\"Fetches the given method's response returning from RenRen API.\n\n        Send a POST request to the given method with the given params.\n        \"\"\"\n        params[\"api_key\"] = self.api_key\n        params[\"call_id\"] = str(int(time.time() * 1000))\n        params[\"format\"] = \"json\"\n        params[\"session_key\"] = self.session_key\n        params[\"v\"] = '1.0'\n        sig = self.hash_params(params);\n        params[\"sig\"] = sig\n        \n        post_data = None if params is None else urllib.urlencode(params)\n        \n        #logging.info(\"request params are: \" + str(post_data))\n        \n        file = urllib.urlopen(RENREN_API_SERVER, post_data)\n        \n        try:\n            s = file.read()\n            logging.info(\"api response is: \" + s)\n            response = _parse_json(s)\n        finally:\n            file.close()\n        if type(response) is not list and response[\"error_code\"]:\n            logging.info(response[\"error_msg\"])\n            raise RenRenAPIError(response[\"error_code\"], response[\"error_msg\"])\n        return response\n    def hash_params(self, params = None):\n        hasher = hashlib.md5(\"\".join([\"%s=%s\" % (self.unicode_encode(x), self.unicode_encode(params[x])) for x in sorted(params.keys())]))\n        hasher.update(self.secret_key)\n        return hasher.hexdigest()\n    def unicode_encode(self, str):\n        \"\"\"\n        Detect if a string is unicode and encode as utf-8 if necessary\n        \"\"\"\n        return isinstance(str, unicode) and str.encode('utf-8') or str\n    \nclass RenRenAPIError(Exception):\n    def __init__(self, code, message):\n        Exception.__init__(self, message)\n        self.code = code\n\ndef set_cookie(response, name, value, domain=None, path=\"/\", expires=None):\n    \"\"\"Generates and signs a cookie for the give name/value\"\"\"\n    # Now we just ignore domain, path and expires\n    response.set_secure_cookie(name, value)\n    logging.info(\"set cookie as \" + name + \", value is: \" + value)\n\ndef parse_cookie(value):\n    \"\"\"Parses and verifies a cookie value from set_cookie\"\"\"\n    if not value: return None\n    return value\n\nsettings = {\n  \"debug\": True,\n  \"cookie_secret\": \"c19e4cc825adee8ab0928244186538aca2821425\",\n  \"static_path\": os.path.join(os.path.dirname(__file__))\n}\n\napp = tornado.wsgi.WSGIApplication([\n    (r\"/\", HomeHandler),\n    (r\"/auth/login\", LoginHandler),\n    (r\"/auth/logout\", LogoutHandler),\n], **settings)\n\nif __name__ == '__main__':\n    import wsgiref.simple_server\n    httpd = wsgiref.simple_server.make_server('', 8080, app)\n    httpd.serve_forever()\n"
  },
  {
    "path": "examples/segment/config.yaml",
    "content": "name: pylabs\nversion: 3\n"
  },
  {
    "path": "examples/segment/index.wsgi",
    "content": "#-*-coding: utf8 -*-\n\n\"\"\"\n分词服务请求\n------------\n\nSAE分词服务请求采用以下形式的HTTP网址： ::\n\n    http://segment.sae.sina.com.cn/urlclient.php?parameters\n\nparameters为请求参数，多个参数之间使用&分割，以下列出了这些参数和其可能的值。\n\n* word_tag: 是否返回词性数据。0表示不返回，1表示返回，默认为0不返回。\n* encoding: 请求分词的文本的编码，可以为: GB18030、UTF-8、UCS-2，默认为UTF-8。\n\n请求分词的文本以post的形式提交。\n\n* context: 请求分词的文本。目前限制文本大小最大为10KB。\n\n分词服务响应\n------------\n\n分词服务的响应数据为json格式，格式如下： ::\n\n    [\n        {\"word\":\"采莲\",\"word_tag\":\"171\",\"index\":\"1\"},\n        {\"word\":\"赋\",\"word_tag\":\"170\",\"index\":\"2\"}\n    ]\n\n响应数据为一个list，list中每个元素为一个dict，每个dict中包含以下数据：\n\n* index: 序列号，按在请求文本中的位置依次递增。\n* word: 单词\n* word_tag: 单词的词性，仅当输入parameters里word_tag为1时包含该项。\n\n词性代码： ::\n\n    0   POSTAG_ID_UNKNOW 未知\n    10  POSTAG_ID_A      形容词\n    20  POSTAG_ID_B      区别词\n    30  POSTAG_ID_C      连词\n    31  POSTAG_ID_C_N    体词连接\n    32  POSTAG_ID_C_Z    分句连接\n    40  POSTAG_ID_D      副词\n    41  POSTAG_ID_D_B    副词(\"不\")\n    42  POSTAG_ID_D_M    副词(\"没\")\n    50  POSTAG_ID_E      叹词\n    60  POSTAG_ID_F      方位词\n    61  POSTAG_ID_F_S    方位短语(处所词+方位词)\n    62  POSTAG_ID_F_N    方位短语(名词+方位词“地上”)\n    63  POSTAG_ID_F_V    方位短语(动词+方位词“取前”)\n    64  POSTAG_ID_F_Z    方位短语(动词+方位词“取前”)\n    70  POSTAG_ID_H      前接成分\n    71  POSTAG_ID_H_M    数词前缀(“数”---数十)\n    72  POSTAG_ID_H_T    时间词前缀(“公元”“明永乐”)\n    73  POSTAG_ID_H_NR   姓氏\n    74  POSTAG_ID_H_N    姓氏\n    80  POSTAG_ID_K      后接成分\n    81  POSTAG_ID_K_M    数词后缀(“来”--,十来个)\n    82  POSTAG_ID_K_T    时间词后缀(“初”“末”“时”)\n    83  POSTAG_ID_K_N    名词后缀(“们”)\n    84  POSTAG_ID_K_S    处所词后缀(“苑”“里”)\n    85  POSTAG_ID_K_Z    状态词后缀(“然”)\n    86  POSTAG_ID_K_NT   状态词后缀(“然”)\n    87  POSTAG_ID_K_NS   状态词后缀(“然”)\n    90  POSTAG_ID_M      数词\n    95  POSTAG_ID_N      名词\n    96  POSTAG_ID_N_RZ   人名(“毛泽东”)\n    97  POSTAG_ID_N_T    机构团体(“团”的声母为t，名词代码n和t并在一起。“公司”)\n    98  POSTAG_ID_N_TA   ....\n    99  POSTAG_ID_N_TZ   机构团体名(\"北大\")\n    100 POSTAG_ID_N_Z    其他专名(“专”的声母的第1个字母为z，名词代码n和z并在一起。)\n    101 POSTAG_ID_NS     名处词\n    102 POSTAG_ID_NS_Z   地名(名处词专指：“中国”)\n    103 POSTAG_ID_N_M    n-m,数词开头的名词(三个学生)\n    104 POSTAG_ID_N_RB   n-rb,以区别词/代词开头的名词(该学校，该生)\n    107 POSTAG_ID_O      拟声词\n    108 POSTAG_ID_P      介词\n    110 POSTAG_ID_Q      量词\n    111 POSTAG_ID_Q_V    动量词(“趟”“遍”)\n    112 POSTAG_ID_Q_T    时间量词(“年”“月”“期”)\n    113 POSTAG_ID_Q_H    货币量词(“元”“美元”“英镑”)\n    120 POSTAG_ID_R      代词\n    121 POSTAG_ID_R_D    副词性代词(“怎么”)\n    122 POSTAG_ID_R_M    数词性代词(“多少”)\n    123 POSTAG_ID_R_N    名词性代词(“什么”“谁”)\n    124 POSTAG_ID_R_S    处所词性代词(“哪儿”)\n    125 POSTAG_ID_R_T    时间词性代词(“何时”)\n    126 POSTAG_ID_R_Z    谓词性代词(“怎么样”)\n    127 POSTAG_ID_R_B    区别词性代词(“某”“每”)\n    130 POSTAG_ID_S      处所词(取英语space的第1个字母。“东部”)\n    131 POSTAG_ID_S_Z    处所词(取英语space的第1个字母。“东部”)\n    132 POSTAG_ID_T      时间词(取英语time的第1个字母)\n    133 POSTAG_ID_T_Z    时间专指(“唐代”“西周”)\n    140 POSTAG_ID_U      助词\n    141 POSTAG_ID_U_N    定语助词(“的”)\n    142 POSTAG_ID_U_D    状语助词(“地”)\n    143 POSTAG_ID_U_C    补语助词(“得”)\n    144 POSTAG_ID_U_Z    谓词后助词(“了、着、过”)\n    145 POSTAG_ID_U_S    体词后助词(“等、等等”)\n    146 POSTAG_ID_U_SO   助词(“所”)\n    150 POSTAG_ID_W      标点符号\n    151 POSTAG_ID_W_D    顿号(“、”)\n    152 POSTAG_ID_W_SP   句号(“。”)\n    153 POSTAG_ID_W_S    分句尾标点(“，”“；”)\n    154 POSTAG_ID_W_L    搭配型标点左部\n    155 POSTAG_ID_W_R    搭配型标点右部(“》”“]”“）”)\n    156 POSTAG_ID_W_H    中缀型符号\n    160 POSTAG_ID_Y      语气词(取汉字“语”的声母。“吗”“吧”“啦”)\n    170 POSTAG_ID_V      及物动词(取英语动词verb的第一个字母。)\n    171 POSTAG_ID_V_O    不及物谓词(谓宾结构“剃头”)\n    172 POSTAG_ID_V_E    动补结构动词(“取出”“放到”)\n    173 POSTAG_ID_V_SH   动词“是”\n    174 POSTAG_ID_V_YO   动词“有”\n\n    175 POSTAG_ID_V_Q    趋向动词(“来”“去”“进来”)\n    176 POSTAG_ID_V_A    助动词(“应该”“能够”)\n    180 POSTAG_ID_Z      状态词(不及物动词,v-o、sp之外的不及物动词)\n    190 POSTAG_ID_X      语素字\n    191 POSTAG_ID_X_N    名词语素(“琥”)\n    192 POSTAG_ID_X_V    动词语素(“酹”)\n    193 POSTAG_ID_X_S    处所词语素(“中”“日”“美”)\n    194 POSTAG_ID_X_T    时间词语素(“唐”“宋”“元”)\n    195 POSTAG_ID_X_Z    状态词语素(“伟”“芳”)\n    196 POSTAG_ID_X_B    状态词语素(“伟”“芳”)\n    200 POSTAG_ID_SP     不及物谓词(主谓结构“腰酸”“头疼”)\n    201 POSTAG_ID_MQ     数量短语(“叁个”)\n    202 POSTAG_ID_RQ     代量短语(“这个”)\n    210 POSTAG_ID_AD     副形词(直接作状语的形容词)\n    211 POSTAG_ID_AN     名形词(具有名词功能的形容词)\n    212 POSTAG_ID_VD     副动词(直接作状语的动词)\n    213 POSTAG_ID_VN     名动词(指具有名词功能的动词)\n    230 POSTAG_ID_SPACE  空格\n\"\"\"\n\nimport sae\nimport urllib\nimport urllib2\n\n_SEGMENT_BASE_URL = 'http://segment.sae.sina.com.cn/urlclient.php'\n\nsome_chinese_text = \"\"\"\n采莲赋 （萧绎 ）\n紫茎兮文波，红莲兮芰荷。绿房兮翠盖，素实兮黄螺。于时妖童媛女，荡舟心许，\n（益鸟，音益）首徐回，兼传羽杯。棹将移而藻挂，船欲动而萍开。尔其纤腰束\n素，迁延顾步。夏始春余，叶嫩花初。恐沾裳而浅笑，畏倾船而敛裾，故以水溅\n兰桡，芦侵罗（衤荐，音间）。菊泽未反，梧台迥见，荇湿沾衫，菱长绕钏。泛\n柏舟而容与，歌采莲于江渚。歌曰：“碧玉小家女，来嫁汝南王。莲花乱脸色，\n荷叶杂衣香。因持荐君子，愿袭芙蓉裳。”\n\"\"\"\n\ndef segment(text):\n    payload = urllib.urlencode([('context', text),])\n    args = urllib.urlencode([('word_tag', 1), ('encoding', 'UTF-8'),])\n    url = _SEGMENT_BASE_URL + '?' + args\n    return urllib2.urlopen(url, payload).read()\n\ndef app(environ, start_response):\n    status = '200 OK'\n    response_headers = [('Content-type', 'text/plain')]\n    start_response(status, response_headers)\n\n    output = segment(some_chinese_text)\n\n    return [output]\n\napplication = sae.create_wsgi_app(app)\n"
  },
  {
    "path": "examples/static-site/README",
    "content": "A demo for pure static site with 0 python code.\n"
  },
  {
    "path": "examples/static-site/config.yaml",
    "content": "name: staticsite\nversion: 1\n\nhandlers:\n- url: /\n  static_path: www\n"
  },
  {
    "path": "examples/static-site/www/index.html",
    "content": "<html>\n <head>\n  <title>Hello App Engine!</title>\n </head>\n <body>\n  <img src=\"/appengine_button_noborder.gif\" valign=\"middle\">\n  Hello Sina App Engine!\n </body>\n</html>\n"
  },
  {
    "path": "examples/tornado/async/config.yaml",
    "content": "name: pylabs\nversion: 1\n\nworker: tornado\n"
  },
  {
    "path": "examples/tornado/async/index.wsgi",
    "content": "import tornado.web\nfrom tornado.httpclient import AsyncHTTPClient\n\nclass MainHandler(tornado.web.RequestHandler):\n    @tornado.web.asynchronous\n    def get(self):\n        http = tornado.httpclient.AsyncHTTPClient()\n        http.fetch(\"http://wiki.westeros.org\", callback=self._callback)\n        self.write(\"Hello to the Tornado world! \")\n        self.flush()\n\n    def _callback(self, response):\n        self.write(response.body)\n        self.finish()\n\nsettings = {\n    \"debug\": True,\n}\n\n# application should be an instance of `tornado.web.Application`,\n# and don't wrap it with `sae.create_wsgi_app`\napplication = tornado.web.Application([\n    (r\"/\", MainHandler),\n], **settings)\n"
  },
  {
    "path": "examples/tornado/wsgi/README",
    "content": "Hello, Tornado!\n"
  },
  {
    "path": "examples/tornado/wsgi/index.wsgi",
    "content": "import tornado.wsgi\n\nimport sae\n\nclass MainHandler(tornado.web.RequestHandler):\n    def get(self):\n        self.write(\"Hello, world! - Tornado\")\n\napp = tornado.wsgi.WSGIApplication([\n    (r\"/\", MainHandler),\n])\n\napplication = sae.create_wsgi_app(app)\n"
  },
  {
    "path": "examples/trac/README.md",
    "content": "如何在SAE上安装和运行Trac\n---------------------------\n\n1.  下载本示例代码，进入本代码所在目录，使用以下命令打包安装所有的依赖包\n\n        saecloud install -r requirements.txt\n\n    该命令会下载 `Trac-1.0.1` 并安装到应用的 `site-packages` 目录下。\n\n2.  修改 `project/conf/trac.ini` 中的mysql配置为你的应用的配置。\n\n        [trac]\n        ...\n        database=mysql://$accesskey:$secretkey@w.rdc.sae.sina.com.cn:3307/app_$appname\n\n3.  修改 `config.yaml` 中的应用名为你的应用名，部署应用。\n\n        saecloud deploy\n\n4.  进入应用的MYSQL管理页面，导入 `setup.sql` 。\n\n4.  打开 http://$appname.sinaapp.com 即可看到示例trac的页面了。\n\n5.  删除 `project` 目录，使用 `trac-admin` 创建你自己的项目，根据需要修改 `index.wsgi`\n    中的 `TRAC_ENV` 环境变量即可。\n\n示例页面： http://tractest.sinaapp.com\n"
  },
  {
    "path": "examples/trac/config.yaml",
    "content": "name: tractest\nversion: 1\n"
  },
  {
    "path": "examples/trac/index.wsgi",
    "content": "#-*-coding: utf8 -*-\n\nimport os.path\nroot = os.path.dirname(__file__)\n\nimport sys\nsys.path.insert(0, os.path.join(root, 'site-packages'))\n\nos.environ['TRAC_ENV'] = os.path.join(root, 'project')\n#os.environ['TRAC_ENV_PARENT_DIR'] = os.path.join(root, 'projects')\n\nimport trac.web.main as main\napplication = main.dispatch_request\n"
  },
  {
    "path": "examples/trac/project/README",
    "content": "This directory contains a Trac environment.\nVisit http://trac.edgewall.org/ for more information.\n"
  },
  {
    "path": "examples/trac/project/VERSION",
    "content": "Trac Environment Version 1\n"
  },
  {
    "path": "examples/trac/project/conf/trac.ini",
    "content": "# -*- coding: utf-8 -*-\n\n[attachment]\nmax_size = 262144\nmax_zip_size = 2097152\nrender_unsafe_content = false\n\n[browser]\ncolor_scale = True\ndownloadable_paths = /trunk, /branches/*, /tags/*\nhide_properties = svk:merge\nintermediate_color = \nintermediate_point = \nnewest_color = (255, 136, 136)\noldest_color = (136, 136, 255)\noneliner_properties = trac:summary\nrender_unsafe_content = false\nwiki_properties = trac:description\n\n[changeset]\nmax_diff_bytes = 10000000\nmax_diff_files = 0\nwiki_format_messages = true\n\n[header_logo]\nalt = (please configure the [header_logo] section in trac.ini)\nheight = -1\nlink = \nsrc = site/your_project_logo.png\nwidth = -1\n\n[inherit]\nhtdocs_dir = \nplugins_dir = \ntemplates_dir = \n\n[logging]\nlog_file = trac.log\n# log_format = <inherited>\nlog_level = DEBUG\nlog_type = none\n\n[milestone]\nstats_provider = DefaultTicketGroupStatsProvider\n\n[mimeviewer]\nmax_preview_size = 262144\nmime_map = text/x-dylan:dylan, text/x-idl:ice, text/x-ada:ads:adb\nmime_map_patterns = text/plain:README|INSTALL|COPYING.*\npygments_default_style = trac\npygments_modes = \ntab_width = 8\ntreat_as_binary = application/octet-stream, application/pdf, application/postscript, application/msword,application/rtf,\n\n[notification]\nadmit_domains = \nalways_notify_owner = false\nalways_notify_reporter = false\nalways_notify_updater = true\nambiguous_char_width = single\nbatch_subject_template = $prefix Batch modify: $tickets_descr\nemail_sender = SmtpEmailSender\nignore_domains = \nmime_encoding = none\nsendmail_path = sendmail\nsmtp_always_bcc = \nsmtp_always_cc = \nsmtp_default_domain = \nsmtp_enabled = false\nsmtp_from = trac@localhost\nsmtp_from_author = false\nsmtp_from_name = \nsmtp_password = \nsmtp_port = 25\nsmtp_replyto = trac@localhost\nsmtp_server = localhost\nsmtp_subject_prefix = __default__\nsmtp_user = \nticket_subject_template = $prefix #$ticket.id: $summary\nuse_public_cc = false\nuse_short_addr = false\nuse_tls = false\n\n[project]\nadmin = \nadmin_trac_url = .\ndescr = My example project\nfooter = Visit the Trac open source project at<br /><a href=\"http://trac.edgewall.org/\">http://trac.edgewall.org/</a>\nicon = common/trac.ico\nname = My Project\nurl = \n\n[query]\ndefault_anonymous_query = status!=closed&cc~=$USER\ndefault_query = status!=closed&owner=$USER\nitems_per_page = 100\nticketlink_query = ?status=!closed\n\n[report]\nitems_per_page = 100\nitems_per_page_rss = 0\n\n[revisionlog]\ndefault_log_limit = 100\ngraph_colors = ['#cc0', '#0c0', '#0cc', '#00c', '#c0c', '#c00']\n\n[roadmap]\nstats_provider = DefaultTicketGroupStatsProvider\n\n[search]\n# default_disabled_filters = <inherited>\nmin_query_length = 3\n\n[sqlite]\n# extensions = <inherited>\n\n[ticket]\ndefault_cc = \ndefault_component = \ndefault_description = \ndefault_keywords = \ndefault_milestone = \ndefault_owner = < default >\ndefault_priority = major\ndefault_resolution = fixed\ndefault_severity = \ndefault_summary = \ndefault_type = defect\ndefault_version = \nmax_comment_size = 262144\nmax_description_size = 262144\npreserve_newlines = default\nrestrict_owner = false\nworkflow = ConfigurableTicketWorkflow\n\n[ticket-workflow]\naccept = new,assigned,accepted,reopened -> accepted\naccept.operations = set_owner_to_self\naccept.permissions = TICKET_MODIFY\nleave = * -> *\nleave.default = 1\nleave.operations = leave_status\nreassign = new,assigned,accepted,reopened -> assigned\nreassign.operations = set_owner\nreassign.permissions = TICKET_MODIFY\nreopen = closed -> reopened\nreopen.operations = del_resolution\nreopen.permissions = TICKET_CREATE\nresolve = new,assigned,accepted,reopened -> closed\nresolve.operations = set_resolution\nresolve.permissions = TICKET_MODIFY\n\n[timeline]\nabbreviated_messages = True\nchangeset_collapse_events = false\nchangeset_long_messages = false\nchangeset_show_files = 0\ndefault_daysback = 30\nmax_daysback = 90\nnewticket_formatter = oneliner\nticket_show_details = false\n\n[trac]\nauth_cookie_lifetime = 0\nauth_cookie_path = \nauthz_file = \nauthz_module_name = \nauto_preview_timeout = 2.0\nauto_reload = False\nbackup_dir = db\nbase_url = \ncheck_auth_ip = false\ndatabase = mysql://3mon0ozzn3:5hj2y31jm5xwj1ykh4kkmk2kyxm53h1klk1yxwwy@w.rdc.sae.sina.com.cn:3307/app_tractest\ndebug_sql = False\ndefault_charset = utf-8\ndefault_dateinfo_format = relative\ngenshi_cache_size = 128\nhtdocs_location = \nignore_auth_case = false\njquery_location = \njquery_ui_location = \njquery_ui_theme_location = \nmainnav = wiki, timeline, roadmap, browser, tickets, newticket, search\nmetanav = login, logout, prefs, help, about\nmysqldump_path = mysqldump\nnever_obfuscate_mailto = false\npermission_policies = DefaultPermissionPolicy, LegacyAttachmentPolicy\npermission_store = DefaultPermissionStore\npg_dump_path = pg_dump\nrepository_dir = \nrepository_sync_per_request = (default)\nrepository_type = svn\nresizable_textareas = true\nsecure_cookies = False\nshow_email_addresses = false\nshow_ip_addresses = false\ntimeout = 20\nuse_base_url_for_redirect = False\n\n[versioncontrol]\nallowed_repository_dir_prefixes = \n\n[wiki]\nignore_missing_pages = false\nmax_size = 262144\nrender_unsafe_content = false\nsafe_schemes = cvs, file, ftp, git, irc, http, https, news, sftp, smb, ssh, svn, svn+ssh\nsplit_page_names = false\n\n"
  },
  {
    "path": "examples/trac/project/conf/trac.ini.sample",
    "content": "# -*- coding: utf-8 -*-\n\n[attachment]\nmax_size = 262144\nmax_zip_size = 2097152\nrender_unsafe_content = false\n\n[authz_policy]\nauthz_file = \n\n[browser]\ncolor_scale = True\ndownloadable_paths = /trunk, /branches/*, /tags/*\nhide_properties = svk:merge\nintermediate_color = \nintermediate_point = \nnewest_color = (255, 136, 136)\noldest_color = (136, 136, 255)\noneliner_properties = trac:summary\nrender_unsafe_content = false\nwiki_properties = trac:description\n\n[changeset]\nmax_diff_bytes = 10000000\nmax_diff_files = 0\nwiki_format_messages = true\n\n[git]\ncached_repository = false\ngit_bin = git\ngit_fs_encoding = utf-8\npersistent_cache = false\n# projects_base = <inherited>\n# projects_list = <inherited>\n# projects_url = <inherited>\nshortrev_len = 7\ntrac_user_rlookup = false\nuse_committer_id = true\nuse_committer_time = true\nwikishortrev_len = 40\n\n[header_logo]\nalt = (please configure the [header_logo] section in trac.ini)\nheight = -1\nlink = \nsrc = site/your_project_logo.png\nwidth = -1\n\n[inherit]\nhtdocs_dir = \nplugins_dir = \ntemplates_dir = \n\n[logging]\nlog_file = trac.log\n# log_format = <inherited>\nlog_level = DEBUG\nlog_type = none\n\n[milestone]\nstats_provider = DefaultTicketGroupStatsProvider\n\n[mimeviewer]\nenscript_modes = text/x-dylan:dylan:4\nenscript_path = enscript\nmax_preview_size = 262144\nmime_map = text/x-dylan:dylan, text/x-idl:ice, text/x-ada:ads:adb\nmime_map_patterns = text/plain:README|INSTALL|COPYING.*\nphp_path = php\npygments_default_style = trac\npygments_modes = \ntab_width = 8\ntreat_as_binary = application/octet-stream, application/pdf, application/postscript, application/msword,application/rtf,\n\n[notification]\nadmit_domains = \nalways_notify_owner = false\nalways_notify_reporter = false\nalways_notify_updater = true\nambiguous_char_width = single\nbatch_subject_template = $prefix Batch modify: $tickets_descr\nemail_sender = SmtpEmailSender\nignore_domains = \nmime_encoding = none\nsendmail_path = sendmail\nsmtp_always_bcc = \nsmtp_always_cc = \nsmtp_default_domain = \nsmtp_enabled = false\nsmtp_from = trac@localhost\nsmtp_from_author = false\nsmtp_from_name = \nsmtp_password = \nsmtp_port = 25\nsmtp_replyto = trac@localhost\nsmtp_server = localhost\nsmtp_subject_prefix = __default__\nsmtp_user = \nticket_subject_template = $prefix #$ticket.id: $summary\nuse_public_cc = false\nuse_short_addr = false\nuse_tls = false\n\n[project]\nadmin = \nadmin_trac_url = .\ndescr = My example project\nfooter = Visit the Trac open source project at<br /><a href=\"http://trac.edgewall.org/\">http://trac.edgewall.org/</a>\nicon = common/trac.ico\nname = My Project\nurl = \n\n[query]\ndefault_anonymous_query = status!=closed&cc~=$USER\ndefault_query = status!=closed&owner=$USER\nitems_per_page = 100\nticketlink_query = ?status=!closed\n\n[report]\nitems_per_page = 100\nitems_per_page_rss = 0\n\n[revisionlog]\ndefault_log_limit = 100\ngraph_colors = ['#cc0', '#0c0', '#0cc', '#00c', '#c0c', '#c00']\n\n[roadmap]\nstats_provider = DefaultTicketGroupStatsProvider\n\n[search]\n# default_disabled_filters = <inherited>\nmin_query_length = 3\n\n[sqlite]\n# extensions = <inherited>\n\n[svn]\nbranches = trunk, branches/*\ntags = tags/*\n\n[ticket]\ncommit_ticket_update_check_perms = true\ncommit_ticket_update_commands.close = close closed closes fix fixed fixes\ncommit_ticket_update_commands.refs = addresses re references refs see\ncommit_ticket_update_envelope = \ncommit_ticket_update_notify = true\ndefault_cc = \ndefault_component = \ndefault_description = \ndefault_keywords = \ndefault_milestone = \ndefault_owner = < default >\ndefault_priority = major\ndefault_resolution = fixed\ndefault_severity = \ndefault_summary = \ndefault_type = defect\ndefault_version = \nmax_comment_size = 262144\nmax_description_size = 262144\npreserve_newlines = default\nrestrict_owner = false\nworkflow = ConfigurableTicketWorkflow\n\n[timeline]\nabbreviated_messages = True\nchangeset_collapse_events = false\nchangeset_long_messages = false\nchangeset_show_files = 0\ndefault_daysback = 30\nmax_daysback = 90\nnewticket_formatter = oneliner\nticket_show_details = false\n\n[trac]\nauth_cookie_lifetime = 0\nauth_cookie_path = \nauthz_file = \nauthz_module_name = \nauto_preview_timeout = 2.0\nauto_reload = False\nbackup_dir = db\nbase_url = \ncheck_auth_ip = false\ndatabase = sqlite:db/trac.db\ndebug_sql = False\ndefault_charset = utf-8\ndefault_dateinfo_format = relative\ngenshi_cache_size = 128\nhtdocs_location = \nignore_auth_case = false\njquery_location = \njquery_ui_location = \njquery_ui_theme_location = \nmainnav = wiki, timeline, roadmap, browser, tickets, newticket, search\nmetanav = login, logout, prefs, help, about\nmysqldump_path = mysqldump\nnever_obfuscate_mailto = false\npermission_policies = DefaultPermissionPolicy, LegacyAttachmentPolicy\npermission_store = DefaultPermissionStore\npg_dump_path = pg_dump\nrepository_dir = \nrepository_sync_per_request = (default)\nrepository_type = svn\nresizable_textareas = true\nsecure_cookies = False\nshow_email_addresses = false\nshow_ip_addresses = false\ntimeout = 20\nuse_base_url_for_redirect = False\n\n[versioncontrol]\nallowed_repository_dir_prefixes = \n\n[wiki]\nignore_missing_pages = false\nmax_size = 262144\nrender_unsafe_content = false\nsafe_schemes = cvs, file, ftp, git, irc, http, https, news, sftp, smb, ssh, svn, svn+ssh\nsplit_page_names = false\n\n"
  },
  {
    "path": "examples/trac/project/templates/site.html.sample",
    "content": "<html xmlns=\"http://www.w3.org/1999/xhtml\"\n      xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n      xmlns:py=\"http://genshi.edgewall.org/\"\n      py:strip=\"\">\n  <!--!\n    This file allows customizing the appearance of the Trac installation.\n    Add your customizations here and rename the file to site.html. Note that\n    it will take precedence over a global site.html placed in the directory\n    specified by [inherit] templates_dir.\n\n    More information about site appearance customization can be found here:\n\n      http://trac.edgewall.org/wiki/TracInterfaceCustomization#SiteAppearance\n  -->\n</html>\n"
  },
  {
    "path": "examples/trac/setup.sql",
    "content": "-- phpMyAdmin SQL Dump\n-- version 3.3.2deb1\n-- http://www.phpmyadmin.net\n--\n-- 主机: localhost\n-- 生成日期: 2013 年 03 月 11 日 17:30\n-- 服务器版本: 5.1.41\n-- PHP 版本: 5.3.2-1ubuntu4.11\n\nSET SQL_MODE=\"NO_AUTO_VALUE_ON_ZERO\";\n\n\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;\n/*!40101 SET NAMES utf8 */;\n\n--\n-- 数据库: `app_tractests`\n--\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `attachment`\n--\n\nCREATE TABLE IF NOT EXISTS `attachment` (\n  `type` text COLLATE utf8_bin NOT NULL,\n  `id` text COLLATE utf8_bin NOT NULL,\n  `filename` text COLLATE utf8_bin NOT NULL,\n  `size` int(11) DEFAULT NULL,\n  `time` bigint(20) DEFAULT NULL,\n  `description` text COLLATE utf8_bin,\n  `author` text COLLATE utf8_bin,\n  `ipnr` text COLLATE utf8_bin,\n  PRIMARY KEY (`type`(111),`id`(111),`filename`(111))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `attachment`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `auth_cookie`\n--\n\nCREATE TABLE IF NOT EXISTS `auth_cookie` (\n  `cookie` text COLLATE utf8_bin NOT NULL,\n  `name` text COLLATE utf8_bin NOT NULL,\n  `ipnr` text COLLATE utf8_bin NOT NULL,\n  `time` int(11) DEFAULT NULL,\n  PRIMARY KEY (`cookie`(111),`ipnr`(111),`name`(111))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `auth_cookie`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `cache`\n--\n\nCREATE TABLE IF NOT EXISTS `cache` (\n  `id` int(11) NOT NULL DEFAULT '0',\n  `generation` int(11) DEFAULT NULL,\n  `key` text COLLATE utf8_bin,\n  PRIMARY KEY (`id`)\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `cache`\n--\n\nINSERT INTO `cache` (`id`, `generation`, `key`) VALUES\n(901198563, 57, 'trac.wiki.api.WikiSystem.pages');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `component`\n--\n\nCREATE TABLE IF NOT EXISTS `component` (\n  `name` text COLLATE utf8_bin NOT NULL,\n  `owner` text COLLATE utf8_bin,\n  `description` text COLLATE utf8_bin,\n  PRIMARY KEY (`name`(255))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `component`\n--\n\nINSERT INTO `component` (`name`, `owner`, `description`) VALUES\n('component1', 'somebody', NULL),\n('component2', 'somebody', NULL);\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `enum`\n--\n\nCREATE TABLE IF NOT EXISTS `enum` (\n  `type` text COLLATE utf8_bin NOT NULL,\n  `name` text COLLATE utf8_bin NOT NULL,\n  `value` text COLLATE utf8_bin,\n  PRIMARY KEY (`type`(166),`name`(166))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `enum`\n--\n\nINSERT INTO `enum` (`type`, `name`, `value`) VALUES\n('resolution', 'fixed', '1'),\n('resolution', 'invalid', '2'),\n('resolution', 'wontfix', '3'),\n('resolution', 'duplicate', '4'),\n('resolution', 'worksforme', '5'),\n('priority', 'blocker', '1'),\n('priority', 'critical', '2'),\n('priority', 'major', '3'),\n('priority', 'minor', '4'),\n('priority', 'trivial', '5'),\n('ticket_type', 'defect', '1'),\n('ticket_type', 'enhancement', '2'),\n('ticket_type', 'task', '3');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `milestone`\n--\n\nCREATE TABLE IF NOT EXISTS `milestone` (\n  `name` text COLLATE utf8_bin NOT NULL,\n  `due` bigint(20) DEFAULT NULL,\n  `completed` bigint(20) DEFAULT NULL,\n  `description` text COLLATE utf8_bin,\n  PRIMARY KEY (`name`(255))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `milestone`\n--\n\nINSERT INTO `milestone` (`name`, `due`, `completed`, `description`) VALUES\n('milestone1', 0, 0, NULL),\n('milestone2', 0, 0, NULL),\n('milestone3', 0, 0, NULL),\n('milestone4', 0, 0, NULL);\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `node_change`\n--\n\nCREATE TABLE IF NOT EXISTS `node_change` (\n  `repos` int(11) NOT NULL DEFAULT '0',\n  `rev` text COLLATE utf8_bin NOT NULL,\n  `path` text COLLATE utf8_bin NOT NULL,\n  `node_type` text COLLATE utf8_bin,\n  `change_type` text COLLATE utf8_bin NOT NULL,\n  `base_path` text COLLATE utf8_bin,\n  `base_rev` text COLLATE utf8_bin,\n  PRIMARY KEY (`repos`,`rev`(20),`path`(255),`change_type`(2)),\n  KEY `node_change_repos_rev_idx` (`repos`,`rev`(20))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `node_change`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `permission`\n--\n\nCREATE TABLE IF NOT EXISTS `permission` (\n  `username` text COLLATE utf8_bin NOT NULL,\n  `action` text COLLATE utf8_bin NOT NULL,\n  PRIMARY KEY (`username`(166),`action`(166))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `permission`\n--\n\nINSERT INTO `permission` (`username`, `action`) VALUES\n('anonymous', 'LOG_VIEW'),\n('anonymous', 'FILE_VIEW'),\n('anonymous', 'WIKI_VIEW'),\n('authenticated', 'WIKI_CREATE'),\n('authenticated', 'WIKI_MODIFY'),\n('anonymous', 'SEARCH_VIEW'),\n('anonymous', 'REPORT_VIEW'),\n('anonymous', 'REPORT_SQL_VIEW'),\n('anonymous', 'TICKET_VIEW'),\n('authenticated', 'TICKET_CREATE'),\n('authenticated', 'TICKET_MODIFY'),\n('anonymous', 'BROWSER_VIEW'),\n('anonymous', 'TIMELINE_VIEW'),\n('anonymous', 'CHANGESET_VIEW'),\n('anonymous', 'ROADMAP_VIEW'),\n('anonymous', 'MILESTONE_VIEW');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `report`\n--\n\nCREATE TABLE IF NOT EXISTS `report` (\n  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,\n  `author` text COLLATE utf8_bin,\n  `title` text COLLATE utf8_bin,\n  `query` text COLLATE utf8_bin,\n  `description` text COLLATE utf8_bin,\n  PRIMARY KEY (`id`)\n) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=9 ;\n\n--\n-- 转存表中的数据 `report`\n--\n\nINSERT INTO `report` (`id`, `author`, `title`, `query`, `description`) VALUES\n(1, NULL, 'Active Tickets', 'SELECT p.value AS __color__,\\n   id AS ticket, summary, component, version, milestone, t.type AS type,\\n   owner, status,\\n   time AS created,\\n   changetime AS _changetime, description AS _description,\\n   reporter AS _reporter\\n  FROM ticket t\\n  LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority''\\n  WHERE status <> ''closed''\\n  ORDER BY CAST(p.value AS signed), milestone, t.type, time\\n', ' * List all active tickets by priority.\\n * Color each row based on priority.\\n'),\n(2, NULL, 'Active Tickets by Version', 'SELECT p.value AS __color__,\\n   version AS __group__,\\n   id AS ticket, summary, component, version, t.type AS type,\\n   owner, status,\\n   time AS created,\\n   changetime AS _changetime, description AS _description,\\n   reporter AS _reporter\\n  FROM ticket t\\n  LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority''\\n  WHERE status <> ''closed''\\n  ORDER BY (version IS NULL),version, CAST(p.value AS signed), t.type, time\\n', 'This report shows how to color results by priority,\\nwhile grouping results by version.\\n\\nLast modification time, description and reporter are included as hidden fields\\nfor useful RSS export.\\n'),\n(3, NULL, 'Active Tickets by Milestone', 'SELECT p.value AS __color__,\\n   concat(''Milestone '', milestone) AS __group__,\\n   id AS ticket, summary, component, version, t.type AS type,\\n   owner, status,\\n   time AS created,\\n   changetime AS _changetime, description AS _description,\\n   reporter AS _reporter\\n  FROM ticket t\\n  LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority''\\n  WHERE status <> ''closed''\\n  ORDER BY (milestone IS NULL),milestone, CAST(p.value AS signed), t.type, time\\n', 'This report shows how to color results by priority,\\nwhile grouping results by milestone.\\n\\nLast modification time, description and reporter are included as hidden fields\\nfor useful RSS export.\\n'),\n(4, NULL, 'Accepted, Active Tickets by Owner', 'SELECT p.value AS __color__,\\n   owner AS __group__,\\n   id AS ticket, summary, component, milestone, t.type AS type, time AS created,\\n   changetime AS _changetime, description AS _description,\\n   reporter AS _reporter\\n  FROM ticket t\\n  LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority''\\n  WHERE status = ''accepted''\\n  ORDER BY owner, CAST(p.value AS signed), t.type, time\\n', 'List accepted tickets, group by ticket owner, sorted by priority.\\n'),\n(5, NULL, 'Accepted, Active Tickets by Owner (Full Description)', 'SELECT p.value AS __color__,\\n   owner AS __group__,\\n   id AS ticket, summary, component, milestone, t.type AS type, time AS created,\\n   description AS _description_,\\n   changetime AS _changetime, reporter AS _reporter\\n  FROM ticket t\\n  LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority''\\n  WHERE status = ''accepted''\\n  ORDER BY owner, CAST(p.value AS signed), t.type, time\\n', 'List tickets accepted, group by ticket owner.\\nThis report demonstrates the use of full-row display.\\n'),\n(6, NULL, 'All Tickets By Milestone  (Including closed)', 'SELECT p.value AS __color__,\\n   t.milestone AS __group__,\\n   (CASE status\\n      WHEN ''closed'' THEN ''color: #777; background: #ddd; border-color: #ccc;''\\n      ELSE\\n        (CASE owner WHEN $USER THEN ''font-weight: bold'' END)\\n    END) AS __style__,\\n   id AS ticket, summary, component, status,\\n   resolution,version, t.type AS type, priority, owner,\\n   changetime AS modified,\\n   time AS _time,reporter AS _reporter\\n  FROM ticket t\\n  LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority''\\n  ORDER BY (milestone IS NULL), milestone DESC, (status = ''closed''),\\n        (CASE status WHEN ''closed'' THEN changetime ELSE (-1) * CAST(p.value AS signed) END) DESC\\n', 'A more complex example to show how to make advanced reports.\\n'),\n(7, NULL, 'My Tickets', 'SELECT DISTINCT\\n       p.value AS __color__,\\n       (CASE\\n         WHEN owner = $USER AND status = ''accepted'' THEN ''Accepted''\\n         WHEN owner = $USER THEN ''Owned''\\n         WHEN reporter = $USER THEN ''Reported''\\n         ELSE ''Commented'' END) AS __group__,\\n       t.id AS ticket, summary, component, version, milestone,\\n       t.type AS type, priority, t.time AS created,\\n       t.changetime AS _changetime, description AS _description,\\n       reporter AS _reporter\\n  FROM ticket t\\n  LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority''\\n  LEFT JOIN ticket_change tc ON tc.ticket = t.id AND tc.author = $USER\\n                                AND tc.field = ''comment''\\n  WHERE t.status <> ''closed''\\n        AND (owner = $USER OR reporter = $USER OR author = $USER)\\n  ORDER BY (owner = $USER AND status = ''accepted'') DESC,\\n           owner = $USER DESC, reporter = $USER DESC,\\n           CAST(p.value AS signed), milestone, t.type, t.time\\n', 'This report demonstrates the use of the automatically set\\nUSER dynamic variable, replaced with the username of the\\nlogged in user when executed.\\n'),\n(8, NULL, 'Active Tickets, Mine first', 'SELECT p.value AS __color__,\\n   (CASE owner\\n     WHEN $USER THEN ''My Tickets''\\n     ELSE ''Active Tickets''\\n    END) AS __group__,\\n   id AS ticket, summary, component, version, milestone, t.type AS type,\\n   owner, status,\\n   time AS created,\\n   changetime AS _changetime, description AS _description,\\n   reporter AS _reporter\\n  FROM ticket t\\n  LEFT JOIN enum p ON p.name = t.priority AND p.type = ''priority''\\n  WHERE status <> ''closed''\\n  ORDER BY (COALESCE(owner, '''') = $USER) DESC, CAST(p.value AS signed), milestone, t.type, time\\n', ' * List all active tickets by priority.\\n * Show all tickets owned by the logged in user in a group first.\\n');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `repository`\n--\n\nCREATE TABLE IF NOT EXISTS `repository` (\n  `id` int(11) NOT NULL DEFAULT '0',\n  `name` text COLLATE utf8_bin NOT NULL,\n  `value` text COLLATE utf8_bin,\n  PRIMARY KEY (`id`,`name`(166))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `repository`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `revision`\n--\n\nCREATE TABLE IF NOT EXISTS `revision` (\n  `repos` int(11) NOT NULL DEFAULT '0',\n  `rev` text COLLATE utf8_bin NOT NULL,\n  `time` bigint(20) DEFAULT NULL,\n  `author` text COLLATE utf8_bin,\n  `message` text COLLATE utf8_bin,\n  PRIMARY KEY (`repos`,`rev`(20)),\n  KEY `revision_repos_time_idx` (`repos`,`time`)\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `revision`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `session`\n--\n\nCREATE TABLE IF NOT EXISTS `session` (\n  `sid` text COLLATE utf8_bin NOT NULL,\n  `authenticated` int(11) NOT NULL DEFAULT '0',\n  `last_visit` int(11) DEFAULT NULL,\n  PRIMARY KEY (`sid`(166),`authenticated`),\n  KEY `session_last_visit_idx` (`last_visit`),\n  KEY `session_authenticated_idx` (`authenticated`)\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `session`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `session_attribute`\n--\n\nCREATE TABLE IF NOT EXISTS `session_attribute` (\n  `sid` text COLLATE utf8_bin NOT NULL,\n  `authenticated` int(11) NOT NULL DEFAULT '0',\n  `name` text COLLATE utf8_bin NOT NULL,\n  `value` text COLLATE utf8_bin,\n  PRIMARY KEY (`sid`(111),`authenticated`,`name`(111))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `session_attribute`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `system`\n--\n\nCREATE TABLE IF NOT EXISTS `system` (\n  `name` text COLLATE utf8_bin NOT NULL,\n  `value` text COLLATE utf8_bin,\n  PRIMARY KEY (`name`(255))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `system`\n--\n\nINSERT INTO `system` (`name`, `value`) VALUES\n('database_version', '29'),\n('initial_database_version', '29');\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `ticket`\n--\n\nCREATE TABLE IF NOT EXISTS `ticket` (\n  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,\n  `type` text COLLATE utf8_bin,\n  `time` bigint(20) DEFAULT NULL,\n  `changetime` bigint(20) DEFAULT NULL,\n  `component` text COLLATE utf8_bin,\n  `severity` text COLLATE utf8_bin,\n  `priority` text COLLATE utf8_bin,\n  `owner` text COLLATE utf8_bin,\n  `reporter` text COLLATE utf8_bin,\n  `cc` text COLLATE utf8_bin,\n  `version` text COLLATE utf8_bin,\n  `milestone` text COLLATE utf8_bin,\n  `status` text COLLATE utf8_bin,\n  `resolution` text COLLATE utf8_bin,\n  `summary` text COLLATE utf8_bin,\n  `description` text COLLATE utf8_bin,\n  `keywords` text COLLATE utf8_bin,\n  PRIMARY KEY (`id`),\n  KEY `ticket_time_idx` (`time`),\n  KEY `ticket_status_idx` (`status`(255))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1 ;\n\n--\n-- 转存表中的数据 `ticket`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `ticket_change`\n--\n\nCREATE TABLE IF NOT EXISTS `ticket_change` (\n  `ticket` int(11) NOT NULL DEFAULT '0',\n  `time` bigint(20) NOT NULL DEFAULT '0',\n  `author` text COLLATE utf8_bin,\n  `field` text COLLATE utf8_bin NOT NULL,\n  `oldvalue` text COLLATE utf8_bin,\n  `newvalue` text COLLATE utf8_bin,\n  PRIMARY KEY (`ticket`,`time`,`field`(111)),\n  KEY `ticket_change_ticket_idx` (`ticket`),\n  KEY `ticket_change_time_idx` (`time`)\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `ticket_change`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `ticket_custom`\n--\n\nCREATE TABLE IF NOT EXISTS `ticket_custom` (\n  `ticket` int(11) NOT NULL DEFAULT '0',\n  `name` text COLLATE utf8_bin NOT NULL,\n  `value` text COLLATE utf8_bin,\n  PRIMARY KEY (`ticket`,`name`(166))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `ticket_custom`\n--\n\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `version`\n--\n\nCREATE TABLE IF NOT EXISTS `version` (\n  `name` text COLLATE utf8_bin NOT NULL,\n  `time` bigint(20) DEFAULT NULL,\n  `description` text COLLATE utf8_bin,\n  PRIMARY KEY (`name`(255))\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `version`\n--\n\nINSERT INTO `version` (`name`, `time`, `description`) VALUES\n('1.0', 0, NULL),\n('2.0', 0, NULL);\n\n-- --------------------------------------------------------\n\n--\n-- 表的结构 `wiki`\n--\n\nCREATE TABLE IF NOT EXISTS `wiki` (\n  `name` text COLLATE utf8_bin NOT NULL,\n  `version` int(11) NOT NULL DEFAULT '0',\n  `time` bigint(20) DEFAULT NULL,\n  `author` text COLLATE utf8_bin,\n  `ipnr` text COLLATE utf8_bin,\n  `text` text COLLATE utf8_bin,\n  `comment` text COLLATE utf8_bin,\n  `readonly` int(11) DEFAULT NULL,\n  PRIMARY KEY (`name`(166),`version`),\n  KEY `wiki_time_idx` (`time`)\n) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\n--\n-- 转存表中的数据 `wiki`\n--\n\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('TracCgi', 1, 1362994208158244, 'trac', '127.0.0.1', '= Installing Trac as CGI =\\r\\n\\r\\n{{{\\r\\n#!div class=important\\r\\n  ''''Please note that using Trac via CGI is the slowest deployment method available. It is slower than [TracModPython mod_python], [TracFastCgi FastCGI] and even [trac:TracOnWindowsIisAjp IIS/AJP] on Windows.''''\\r\\n}}}\\r\\n\\r\\nCGI script is the entrypoint that web-server calls when a web-request to an application is made. To generate the `trac.cgi` script run:\\r\\n{{{\\r\\ntrac-admin /path/to/env deploy /path/to/www/trac\\r\\n}}}\\r\\n`trac.cgi` will be in the `cgi-bin` folder inside the given path. ''''Make sure it is executable by your web server''''. This command also copies `static resource` files to a `htdocs` directory of a given destination.\\r\\n\\r\\n== Apache web-server configuration ==\\r\\n\\r\\nIn [http://httpd.apache.org/ Apache] there are two ways to run Trac as CGI:\\r\\n\\r\\n 1. Use a `ScriptAlias` directive that maps an URL to the `trac.cgi` script (recommended)\\r\\n 2. Copy the `trac.cgi` file into the directory for CGI executables used by your web server (commonly named `cgi-bin`). You can also create a symbolic link, but in that case make sure that the `FollowSymLinks` option is enabled for the `cgi-bin` directory.\\r\\n\\r\\nTo make Trac available at `http://yourhost.example.org/trac` add `ScriptAlias` directive to Apache configuration file, changing `trac.cgi` path to match your installation:\\r\\n{{{\\r\\nScriptAlias /trac /path/to/www/trac/cgi-bin/trac.cgi\\r\\n}}}\\r\\n\\r\\n ''''Note that this directive requires enabled `mod_alias` module.''''\\r\\n\\r\\nIf you''re using Trac with a single project you need to set its location using the `TRAC_ENV` environment variable:\\r\\n{{{\\r\\n<Location \"/trac\">\\r\\n  SetEnv TRAC_ENV \"/path/to/projectenv\"\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nOr to use multiple projects you can specify their common parent directory using the `TRAC_ENV_PARENT_DIR` variable:\\r\\n{{{\\r\\n<Location \"/trac\">\\r\\n  SetEnv TRAC_ENV_PARENT_DIR \"/path/to/project/parent/dir\"\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\n ''''Note that the `SetEnv` directive requires enabled `mod_env` module. It is also possible to set TRAC_ENV in trac.cgi. Just add the following code between \"try:\" and \"from trac.web ...\":''''\\r\\n\\r\\n{{{\\r\\n    import os\\r\\n    os.environ[''TRAC_ENV''] = \"/path/to/projectenv\"\\r\\n}}}\\r\\n\\r\\n '''' Or for TRAC_ENV_PARENT_DIR: ''''\\r\\n\\r\\n{{{\\r\\n    import os\\r\\n    os.environ[''TRAC_ENV_PARENT_DIR''] = \"/path/to/project/parent/dir\"\\r\\n}}}\\r\\n\\r\\nIf you are using the [http://httpd.apache.org/docs/suexec.html Apache suEXEC] feature please see [http://trac.edgewall.org/wiki/ApacheSuexec].\\r\\n\\r\\nOn some systems, you ''''may'''' need to edit the shebang line in the `trac.cgi` file to point to your real Python installation path. On a Windows system you may need to configure Windows to know how to execute a .cgi file (Explorer -> Tools -> Folder Options -> File Types -> CGI).\\r\\n\\r\\n=== Using WSGI ===\\r\\n\\r\\nYou can run a [http://henry.precheur.org/python/how_to_serve_cgi WSGI handler] [http://pythonweb.org/projects/webmodules/doc/0.5.3/html_multipage/lib/example-webserver-web-wsgi-simple-cgi.html under CGI].  You can [wiki:TracModWSGI#Thetrac.wsgiscript write your own application function], or use the deployed trac.wsgi''s application.\\r\\n\\r\\n== Mapping Static Resources ==\\r\\n\\r\\nSee TracInstall#MappingStaticResources.\\r\\n\\r\\n== Adding Authentication ==\\r\\n\\r\\nSee TracInstall#ConfiguringAuthentication.\\r\\n\\r\\n----\\r\\nSee also:  TracGuide, TracInstall, [wiki:TracModWSGI], TracFastCgi, TracModPython', NULL, NULL),\n('TracModPython', 1, 1362994208204148, 'trac', '127.0.0.1', '= Trac and mod_python =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nTrac supports [http://www.modpython.org/ mod_python], which speeds up Trac''s response times considerably, especially compared to [TracCgi CGI], and permits use of many Apache features not possible with [wiki:TracStandalone tracd]/mod_proxy.\\r\\n\\r\\n{{{#!div class=\"important\"\\r\\n** A Word of Warning **\\r\\n\\r\\nAs of 16^th^ June 2010, the mod_python project is officially dead.  If you are considering using mod_python for a new installation, ''''''please don''t''''''!  There are known issues which will not be fixed and there are now better alternatives.  Check out the main TracInstall pages for your target version for more information.\\r\\n}}}\\r\\n\\r\\n\\r\\nThese instructions are for Apache 2; if you are still using Apache 1.3, you may have some luck with [trac:wiki:TracModPython2.7 TracModPython2.7], but you''ll be totally on your own.\\r\\n\\r\\n[[PageOutline(2-3,Overview,inline)]]\\r\\n\\r\\n== Simple configuration: single project == #Simpleconfiguration\\r\\n\\r\\nIf you just installed mod_python, you may have to add a line to load the module in the Apache configuration:\\r\\n{{{\\r\\nLoadModule python_module modules/mod_python.so\\r\\n}}}\\r\\n\\r\\n''''Note: The exact path to the module depends on how the HTTPD installation is laid out.''''\\r\\n\\r\\nOn Debian using apt-get\\r\\n{{{\\r\\napt-get install libapache2-mod-python libapache2-mod-python-doc\\r\\n}}}\\r\\n(Still on Debian) after you have installed mod_python, you must enable the modules in apache2 (equivalent of the above Load Module directive):\\r\\n{{{\\r\\na2enmod python\\r\\n}}}\\r\\nOn Fedora use, using yum:\\r\\n{{{\\r\\nyum install mod_python\\r\\n}}}\\r\\nYou can test your mod_python installation by adding the following to your httpd.conf.  You should remove this when you are done testing for security reasons. Note: mod_python.testhandler is only available in mod_python 3.2+.\\r\\n{{{\\r\\n#!xml\\r\\n<Location /mpinfo>\\r\\n   SetHandler mod_python\\r\\n   PythonInterpreter main_interpreter\\r\\n   PythonHandler mod_python.testhandler\\r\\n   Order allow,deny\\r\\n   Allow from all\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nA simple setup of Trac on mod_python looks like this:\\r\\n{{{\\r\\n#!xml\\r\\n<Location /projects/myproject>\\r\\n   SetHandler mod_python\\r\\n   PythonInterpreter main_interpreter\\r\\n   PythonHandler trac.web.modpython_frontend \\r\\n   PythonOption TracEnv /var/trac/myproject\\r\\n   PythonOption TracUriRoot /projects/myproject\\r\\n   Order allow,deny\\r\\n   Allow from all\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nThe option ''''''`TracUriRoot`'''''' may or may not be necessary in your setup. Try your configuration without it; if the URLs produced by Trac look wrong, if Trac does not seem to recognize URLs correctly, or you get an odd \"No handler matched request to...\" error, add the ''''''`TracUriRoot`'''''' option.  You will notice that the `Location` and ''''''`TracUriRoot`'''''' have the same path.\\r\\n\\r\\nThe options available are\\r\\n{{{\\r\\n    # For a single project\\r\\n    PythonOption TracEnv /var/trac/myproject\\r\\n\\r\\n    # For multiple projects\\r\\n    PythonOption TracEnvParentDir /var/trac/myprojects\\r\\n\\r\\n    # For the index of multiple projects\\r\\n    PythonOption TracEnvIndexTemplate /srv/www/htdocs/trac/project_list_template.html\\r\\n\\r\\n    # A space delimitted list, with a \",\" between key and value pairs.\\r\\n    PythonOption TracTemplateVars key1,val1 key2,val2\\r\\n\\r\\n    # Useful to get the date in the wanted order\\r\\n    PythonOption TracLocale en_GB.UTF8\\r\\n\\r\\n    # See description above        \\r\\n    PythonOption TracUriRoot /projects/myproject\\r\\n}}}\\r\\n\\r\\n=== Python Egg Cache ===\\r\\n\\r\\nCompressed python eggs like Genshi are normally extracted into a directory named `.python-eggs` in the users home directory. Since apache''s home usually is not writable an alternate egg cache directory can be specified like this:\\r\\n{{{\\r\\nPythonOption PYTHON_EGG_CACHE /var/trac/myprojects/egg-cache\\r\\n}}}\\r\\n\\r\\nor you can uncompress the Genshi egg to resolve problems extracting from it.\\r\\n\\r\\n=== Configuring Authentication ===\\r\\n\\r\\nSee corresponding section in the [wiki:TracModWSGI#ConfiguringAuthentication] page.\\r\\n\\r\\n\\r\\n== Advanced Configuration\\r\\n\\r\\n=== Setting the Python Egg Cache ===\\r\\n\\r\\nIf the Egg Cache isn''t writeable by your Web server, you''ll either have to change the permissions, or point Python to a location where Apache can write. This can manifest itself as a ''''500 internal server error'''' and/or a complaint in the syslog. \\r\\n\\r\\n{{{\\r\\n#!xml\\r\\n<Location /projects/myproject>\\r\\n  ...\\r\\n  PythonOption PYTHON_EGG_CACHE /tmp \\r\\n  ...\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\n\\r\\n=== Setting the !PythonPath ===\\r\\n\\r\\nIf the Trac installation isn''t installed in your Python path, you''ll have to tell Apache where to find the Trac mod_python handler  using the `PythonPath` directive:\\r\\n{{{\\r\\n#!xml\\r\\n<Location /projects/myproject>\\r\\n  ...\\r\\n  PythonPath \"sys.path + [''/path/to/trac'']\"\\r\\n  ...\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nBe careful about using the !PythonPath directive, and ''''not'''' `SetEnv PYTHONPATH`, as the latter won''t work.\\r\\n\\r\\n=== Setting up multiple projects ===\\r\\n\\r\\nThe Trac mod_python handler supports a configuration option similar to Subversion''s `SvnParentPath`, called `TracEnvParentDir`:\\r\\n{{{\\r\\n#!xml\\r\\n<Location /projects>\\r\\n  SetHandler mod_python\\r\\n  PythonInterpreter main_interpreter\\r\\n  PythonHandler trac.web.modpython_frontend \\r\\n  PythonOption TracEnvParentDir /var/trac\\r\\n  PythonOption TracUriRoot /projects\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nWhen you request the `/projects` URL, you will get a listing of all subdirectories of the directory you set as `TracEnvParentDir` that look like Trac environment directories. Selecting any project in the list will bring you to the corresponding Trac environment.\\r\\n\\r\\nIf you don''t want to have the subdirectory listing as your projects home page you can use a\\r\\n{{{\\r\\n#!xml\\r\\n<LocationMatch \"/.+/\">\\r\\n}}}\\r\\n\\r\\nThis will instruct Apache to use mod_python for all locations different from root while having the possibility of placing a custom home page for root in your !DocumentRoot folder.\\r\\n\\r\\nYou can also use the same authentication realm for all of the projects using a `<LocationMatch>` directive:\\r\\n{{{\\r\\n#!xml\\r\\n<LocationMatch \"/projects/[^/]+/login\">\\r\\n  AuthType Basic\\r\\n  AuthName \"Trac\"\\r\\n  AuthUserFile /var/trac/.htpasswd\\r\\n  Require valid-user\\r\\n</LocationMatch>\\r\\n}}}\\r\\n\\r\\n=== Virtual Host Configuration ===\\r\\n\\r\\nBelow is the sample configuration required to set up your trac as a virtual server (i.e. when you access it at the URLs like\\r\\n!http://trac.mycompany.com):\\r\\n\\r\\n{{{\\r\\n#!xml\\r\\n<VirtualHost * >\\r\\n    DocumentRoot /var/www/myproject\\r\\n    ServerName trac.mycompany.com\\r\\n    <Location />\\r\\n        SetHandler mod_python\\r\\n        PythonInterpreter main_interpreter\\r\\n        PythonHandler trac.web.modpython_frontend\\r\\n        PythonOption TracEnv /var/trac/myproject\\r\\n        PythonOption TracUriRoot /\\r\\n    </Location>\\r\\n    <Location /login>\\r\\n        AuthType Basic\\r\\n        AuthName \"MyCompany Trac Server\"\\r\\n        AuthUserFile /var/trac/myproject/.htpasswd\\r\\n        Require valid-user\\r\\n    </Location>\\r\\n</VirtualHost>\\r\\n}}}\\r\\n\\r\\nThis does not seem to work in all cases. What you can do if it does not:\\r\\n * Try using `<LocationMatch>` instead of `<Location>`\\r\\n * <Location /> may, in your server setup, refer to the complete host instead of simple the root of the server. This means that everything (including the login directory referenced below) will be sent to python and authentication does not work (i.e. you get the infamous Authentication information missing error). If this applies to you, try using a sub-directory for trac instead of the root (i.e. /web/ and /web/login instead of / and /login).\\r\\n * Depending on apache''s `NameVirtualHost` configuration, you may need to use `<VirtualHost *:80>` instead of `<VirtualHost *>`.\\r\\n\\r\\nFor a virtual host that supports multiple projects replace \"`TracEnv`\" /var/trac/myproject with \"`TracEnvParentDir`\" /var/trac/\\r\\n\\r\\nNote: !DocumentRoot should not point to your Trac project env. As Asmodai wrote on #trac: \"suppose there''s a webserver bug that allows disclosure of !DocumentRoot they could then leech the entire Trac environment\".\\r\\n\\r\\n== Troubleshooting ==\\r\\n\\r\\nIn general, if you get server error pages, you can either check the Apache error log, or enable the `PythonDebug` option:\\r\\n{{{\\r\\n#!xml\\r\\n<Location /projects/myproject>\\r\\n  ...\\r\\n  PythonDebug on\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nFor multiple projects, try restarting the server as well.\\r\\n\\r\\n=== Login Not Working ===\\r\\nIf you''ve used `<Location />` directive, it will override any other directives, as well as `<Location /login>`.\\r\\nThe workaround is to use negation expression as follows (for multi project setups):\\r\\n{{{\\r\\n#!xml\\r\\n#this one for other pages\\r\\n<Location ~ \"/*(?!login)\">\\r\\n   SetHandler mod_python\\r\\n   PythonHandler trac.web.modpython_frontend\\r\\n   PythonOption TracEnvParentDir /projects\\r\\n   PythonOption TracUriRoot /\\r\\n\\r\\n</Location>\\r\\n#this one for login page\\r\\n<Location ~ \"/[^/]+/login\">\\r\\n   SetHandler mod_python\\r\\n   PythonHandler trac.web.modpython_frontend\\r\\n   PythonOption TracEnvParentDir /projects\\r\\n   PythonOption TracUriRoot /\\r\\n\\r\\n   #remove these if you don''t want to force SSL\\r\\n   RewriteEngine On \\r\\n   RewriteCond %{HTTPS} off\\r\\n   RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}\\r\\n\\r\\n   AuthType Basic\\r\\n   AuthName \"Trac\"\\r\\n   AuthUserFile /projects/.htpasswd\\r\\n   Require valid-user\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\n=== Expat-related segmentation faults === #expat\\r\\n\\r\\nThis problem will most certainly hit you on Unix when using Python 2.4.\\r\\nIn Python 2.4, some version of Expat (an XML parser library written in C) is used, \\r\\nand if Apache is using another version, this results in segmentation faults.\\r\\nAs Trac 0.11 is using Genshi, which will indirectly use Expat, that problem\\r\\ncan now hit you even if everything was working fine before with Trac 0.10.\\r\\n\\r\\nSee Graham Dumpleton''s detailed [http://www.dscpl.com.au/wiki/ModPython/Articles/ExpatCausingApacheCrash explanation and workarounds] for the issue.\\r\\n\\r\\n=== Form submission problems ===\\r\\n\\r\\nIf you''re experiencing problems submitting some of the forms in Trac (a common problem is that you get redirected to the start page after submission), check whether your {{{DocumentRoot}}} contains a folder or file with the same path that you mapped the mod_python handler to. For some reason, mod_python gets confused when it is mapped to a location that also matches a static resource.\\r\\n\\r\\n=== Problem with virtual host configuration ===\\r\\n\\r\\nIf the <Location /> directive is used, setting the `DocumentRoot` may result in a ''''403 (Forbidden)'''' error. Either remove the `DocumentRoot` directive, or make sure that accessing the directory it points is allowed (in a corresponding `<Directory>` block).\\r\\n\\r\\nUsing <Location /> together with `SetHandler` resulted in having everything handled by mod_python, which leads to not being able download any CSS or images/icons. I used <Location /trac> `SetHandler None` </Location> to circumvent the problem, though I do not know if this is the most elegant solution.\\r\\n\\r\\n=== Problem with zipped egg ===\\r\\n\\r\\nIt''s possible that your version of mod_python will not import modules from zipped eggs. If you encounter an `ImportError: No module named trac` in your Apache logs but you think everything is where it should be, this might be your problem. Look in your site-packages directory; if the Trac module appears as a ''''file'''' rather than a ''''directory'''', then this might be your problem. To rectify, try installing Trac using the `--always-unzip` option, like this:\\r\\n\\r\\n{{{\\r\\neasy_install --always-unzip Trac-0.12b1.zip\\r\\n}}}\\r\\n\\r\\n=== Using .htaccess ===\\r\\n\\r\\nAlthough it may seem trivial to rewrite the above configuration as a directory in your document root with a `.htaccess` file, this does not work. Apache will append a \"/\" to any Trac URLs, which interferes with its correct operation.\\r\\n\\r\\nIt may be possible to work around this with mod_rewrite, but I failed to get this working. In all, it is more hassle than it is worth. Stick to the provided instructions. :)\\r\\n\\r\\nA success story: For me it worked out-of-box, with following trivial config:\\r\\n{{{#!xml\\r\\nSetHandler mod_python\\r\\nPythonInterpreter main_interpreter\\r\\nPythonHandler trac.web.modpython_frontend \\r\\nPythonOption TracEnv /system/path/to/this/directory\\r\\nPythonOption TracUriRoot /path/on/apache\\r\\n\\r\\nAuthType Basic\\r\\nAuthName \"ProjectName\"\\r\\nAuthUserFile /path/to/.htpasswd\\r\\nRequire valid-user\\r\\n}}}\\r\\n\\r\\nThe `TracUriRoot` is obviously the path you need to enter to the browser to get to the trac (e.g. domain.tld/projects/trac)\\r\\n\\r\\n=== Additional .htaccess help ===\\r\\n\\r\\nIf you are using the .htaccess method you may have additional problems if your trac directory is inheriting .htaccess directives from another.  This may also help to add to your .htaccess file:\\r\\n\\r\\n{{{\\r\\n<IfModule mod_rewrite.c>\\r\\n  RewriteEngine Off\\r\\n</IfModule>\\r\\n}}}\\r\\n\\r\\n=== Platform specific issues\\r\\n==== Win32 Issues ====\\r\\nIf you run trac with mod_python < 3.2 on Windows, uploading attachments will ''''''not'''''' work. This problem is resolved in mod_python 3.1.4 or later, so please upgrade mod_python to fix this.\\r\\n\\r\\n\\r\\n==== OS X issues ====\\r\\n\\r\\nWhen using mod_python on OS X you will not be able to restart Apache using `apachectl restart`. This is apparently fixed in mod_python 3.2, but there''s also a patch available for earlier versions [http://www.dscpl.com.au/projects/vampire/patches.html here].\\r\\n\\r\\n==== SELinux issues ====\\r\\n\\r\\nIf Trac reports something like: ''''Cannot get shared lock on db.lock''''\\r\\nThe security context on the repository may need to be set:\\r\\n\\r\\n{{{\\r\\nchcon -R -h -t httpd_sys_content_t PATH_TO_REPOSITORY\\r\\n}}}\\r\\n\\r\\nSee also [http://subversion.tigris.org/faq.html#reposperms]\\r\\n\\r\\n==== FreeBSD issues ====\\r\\nPay attention to the version of the installed mod_python and sqlite packages. Ports have both the new and old ones, but earlier versions of pysqlite and mod_python won''t integrate as the former requires threaded support in python, and the latter requires a threadless install.\\r\\n\\r\\nIf you compiled and installed apache2, apache wouldn´t support threads (cause it doesn´t work very well on FreeBSD). You could force thread support when running ./configure for apache, using --enable-threads, but this isn´t recommendable.\\r\\nThe best option [http://modpython.org/pipermail/mod_python/2006-September/021983.html seems to be] adding to /usr/local/apache2/bin/ennvars the line \\r\\n\\r\\n{{{\\r\\nexport LD_PRELOAD=/usr/lib/libc_r.so\\r\\n}}}\\r\\n\\r\\n\\r\\n==== Fedora 7 Issues ====\\r\\nMake sure you install the ''python-sqlite2'' package as it seems to be required for TracModPython but not for tracd\\r\\n\\r\\n\\r\\n=== Subversion issues ===\\r\\n\\r\\nIf you get the following Trac Error `Unsupported version control system \"svn\"` only under mod_python, though it works well on the command-line and even with TracStandalone, chances are that you forgot to add the path to the Python bindings with the [TracModPython#ConfiguringPythonPath PythonPath] directive. (The better way is to add a link to the bindings in the Python `site-packages` directory, or create a `.pth` file in that directory.)\\r\\n\\r\\nIf this is not the case, it''s possible that you''re using Subversion libraries that are binary incompatible with the apache ones (an incompatibility of the `apr` libraries is usually the cause). In that case, you also won''t be able to use the svn modules for Apache (`mod_dav_svn`).\\r\\n\\r\\nYou also need a recent version of `mod_python` in order to avoid a runtime error ({{{argument number 2: a ''apr_pool_t *'' is expected}}}) due to the default usage of multiple sub-interpreters. 3.2.8 ''''should'''' work, though it''s probably better to use the workaround described in [trac:#3371 #3371], in order to force the use of the main interpreter:\\r\\n{{{\\r\\nPythonInterpreter main_interpreter\\r\\n}}}\\r\\nThis is anyway the recommended workaround for other well-known issues seen when using the Python bindings for Subversion within mod_python ([trac:#2611 #2611], [trac:#3455 #3455]). See in particular Graham Dumpleton''s comment in [trac:comment:9:ticket:3455 #3455] explaining the issue.\\r\\n\\r\\n=== Page layout issues ===\\r\\n\\r\\nIf the formatting of the Trac pages look weird chances are that the style sheets governing the page layout are not handled properly by the web server. Try adding the following lines to your apache configuration:\\r\\n{{{\\r\\n#!xml\\r\\nAlias /myproject/css \"/usr/share/trac/htdocs/css\"\\r\\n<Location /myproject/css>\\r\\n    SetHandler None\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nNote: For the above configuration to have any effect it must be put after the configuration of your project root location, i.e. {{{<Location /myproject />}}}.\\r\\n\\r\\nAlso, setting `PythonOptimize On` seems to mess up the page headers and footers, in addition to hiding the documentation for macros and plugins (see #Trac8956). Considering how little effect the option has, it is probably a good idea to leave it `Off`.\\r\\n\\r\\n=== HTTPS issues ===\\r\\n\\r\\nIf you want to run Trac fully under https you might find that it tries to redirect to plain http. In this case just add the following line to your apache configuration:\\r\\n{{{\\r\\n#!xml\\r\\n<VirtualHost * >\\r\\n    DocumentRoot /var/www/myproject\\r\\n    ServerName trac.mycompany.com\\r\\n    SetEnv HTTPS 1\\r\\n    ....\\r\\n</VirtualHost>\\r\\n}}}\\r\\n\\r\\n\\r\\n=== Segmentation fault with php5-mhash or other php5 modules ===\\r\\nYou may encounter segfaults (reported on debian etch) if php5-mhash module is installed. Try to remove it to see if this solves the problem. See debian bug report [http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=411487]\\r\\n\\r\\nSome people also have troubles when using php5 compiled with its own 3rd party libraries instead of system libraries. Check here [http://www.djangoproject.com/documentation/modpython/#if-you-get-a-segmentation-fault]\\r\\n\\r\\n----\\r\\nSee also:  TracGuide, TracInstall, [wiki:TracModWSGI ModWSGI], [wiki:TracFastCgi FastCGI],  [trac:TracNginxRecipe TracNginxRecipe]\\r\\n', NULL, NULL),\n('WikiPageNames', 1, 1362994208233297, 'trac', '127.0.0.1', '= Wiki Page Names =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nWiki page names commonly use the CamelCase convention. Within wiki text, any word in CamelCase automatically becomes a hyperlink to the wiki page with that name.\\r\\n\\r\\nCamelCase page names must follow these rules:\\r\\n\\r\\n 1. The name must consist of ''''''alphabetic characters only''''''. No digits, spaces, punctuation, or underscores are allowed.\\r\\n 2. A name must have at least two capital letters.\\r\\n 3. The first character must be capitalized.\\r\\n 4. Every capital letter must be followed by one or more lower-case letters. \\r\\n 5. The use of slash ( / ) is permitted in page names (possibly representing a hierarchy).\\r\\n\\r\\nIf you want to create a wiki page that doesn''t follow CamelCase rules you can use the following syntax:\\r\\n{{{\\r\\n * [wiki:Wiki_page], [wiki:ISO9000],\\r\\n   and with a label: [wiki:ISO9000 ISO 9000 standard]\\r\\n * [wiki:\"Space Matters\"]\\r\\n   and with a label: [wiki:\"Space Matters\" all about white space]\\r\\n * or simply: [\"WikiPageName\"]s\\r\\n * even better, the new [[WikiCreole link style]]\\r\\n   and with a label: [[WikiCreole link style|WikiCreole style links]]\\r\\n}}}\\r\\n\\r\\nThis will be rendered as:\\r\\n * [wiki:Wiki_page], [wiki:ISO9000],\\r\\n   and with a label: [wiki:ISO9000 ISO 9000 standard]\\r\\n * [wiki:\"Space Matters\"] ''''(that page name embeds space characters)''''\\r\\n   and with a label: [wiki:\"Space Matters\" all about white space]\\r\\n * or simply: [\"WikiPageName\"]s ''''(old !MoinMoin''s internal free links style)''''\\r\\n * even better, the new [[WikiCreole link style]]\\r\\n   and with a label: [[WikiCreole link style|WikiCreole style links]]\\r\\n   ''''(since 0.12, also now adopted by !MoinMoin)''''\\r\\n\\r\\n\\r\\nStarting with Trac 0.11, it''s also possible to link to a specific ''''version'''' of a Wiki page, as you would do for a specific version of a file, for example: WikiStart@1.\\r\\n\\r\\nYou can also prevent a CamelCase name to be interpreted as a TracLinks, by quoting it. See TracLinks#EscapingLinks.\\r\\n\\r\\nAs visible in the example above, one can also append an anchor to a Wiki page name, in order to link to a specific section within that page. The anchor can easily be seen by hovering the mouse over a section heading, then clicking on the [[html(&para;)]] sign that appears at its end. The anchor is usually generated automatically, but it''s also possible to specify it explicitly: see WikiFormatting#using-explicit-id-in-heading.\\r\\n----\\r\\nSee also: WikiNewPage, WikiFormatting, TracWiki, TracLinks\\r\\n', NULL, NULL),\n('InterWiki', 1, 1362994208251342, 'trac', '127.0.0.1', '= Support for InterWiki links =\\r\\n\\r\\n''''(since [trac:milestone:0.10 0.10])''''\\r\\n\\r\\n== Definition ==\\r\\n\\r\\nAn InterWiki link can be used for referring to a Wiki page\\r\\nlocated in another Wiki system, and by extension, to any object\\r\\nlocated in any other Web application, provided a simple URL \\r\\nmapping can be done.\\r\\n\\r\\nAt the extreme, InterWiki prefixes can even be used to simply introduce\\r\\nlinks to new protocols, such as `tsvn:` used by [trac:TortoiseSvn TortoiseSvn].\\r\\n\\r\\n== Link Syntax ==\\r\\n\\r\\n{{{\\r\\n<target_wiki>(:<identifier>)+\\r\\n}}}\\r\\n\\r\\nThe link is composed by the targeted Wiki (or system) name,\\r\\nfollowed by a colon (e.g. `MeatBall:`),\\r\\nfollowed by a page specification in the target.\\r\\nNote that, as for InterTrac prefixes, ''''''InterWiki prefixes are case insensitive''''''.\\r\\n\\r\\nThe target Wiki URL is looked up in the `[interwiki]` section of TracIni or in the InterMapTxt wiki page, modeled after MeatBall:InterMapTxt. If a prefix is defined in both the `[interwiki]` section and InterMapTxt, the `[interwiki]` section takes precedence.\\r\\n\\r\\nIn addition to traditional InterWiki links, where the target\\r\\nis simply ''''appended'''' to the URL, \\r\\nTrac supports parametric InterWiki URLs:\\r\\nidentifiers `$1`, `$2`, ... in the URL\\r\\nwill be replaced by corresponding arguments.\\r\\nThe argument list is formed by splitting the page identifier\\r\\nusing the \":\" separator.\\r\\n\\r\\n=== [interwiki] ===\\r\\nEvery option in the `[interwiki]` section in TracIni defines one InterWiki prefix. The option name defines the prefix. The option value defines the URL, optionally followed by a description separated from the URL by whitespace. Parametric URLs are supported as well.\\r\\n\\r\\n''''''Example:''''''\\r\\n{{{\\r\\n[interwiki]\\r\\nMeatBall = http://www.usemod.com/cgi-bin/mb.pl?\\r\\nPEP = http://www.python.org/peps/pep-$1.html Python Enhancement Proposal $1\\r\\ntsvn = tsvn: Interact with TortoiseSvn\\r\\n}}}\\r\\n\\r\\n== Examples ==\\r\\n\\r\\nIf the following is an excerpt of the InterMapTxt page:\\r\\n\\r\\n{{{\\r\\n= InterMapTxt =\\r\\n== This is the place for defining InterWiki prefixes ==\\r\\n\\r\\nCurrently active prefixes: [[InterWiki]]\\r\\n\\r\\nThis page is modelled after the MeatBall:InterMapTxt page.\\r\\nIn addition, an optional comment is allowed after the mapping.\\r\\n----\\r\\n{{{\\r\\nPEP      http://www.python.org/peps/pep-$1.html           # Python Enhancement Proposal $1 \\r\\nTrac-ML  http://thread.gmane.org/gmane.comp.version-control.subversion.trac.general/$1  # Message $1 in Trac Mailing List\\r\\n\\r\\ntsvn     tsvn:                                            # Interact with TortoiseSvn\\r\\n...\\r\\nMeatBall http://www.usemod.com/cgi-bin/mb.pl?\\r\\nMetaWiki http://sunir.org/apps/meta.pl?\\r\\nMetaWikiPedia http://meta.wikipedia.org/wiki/\\r\\nMoinMoin http://moinmoin.wikiwikiweb.de/\\r\\n...\\r\\n}}}\\r\\n}}}\\r\\n\\r\\nThen, \\r\\n * `MoinMoin:InterWikiMap` should be rendered as MoinMoin:InterWikiMap\\r\\n   and the ''''title'''' for that link would be \"!InterWikiMap in !MoinMoin\"\\r\\n * `Trac-ML:4346` should be rendered as Trac-ML:4346\\r\\n   and the ''''title'''' for that link would be \"Message 4346 in Trac Mailing List\"\\r\\n\\r\\n----\\r\\nSee also: InterTrac, InterMapTxt\\r\\n', NULL, NULL);\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('WikiFormatting', 1, 1362994208253481, 'trac', '127.0.0.1', '= WikiFormatting =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nWiki markup is a core feature in Trac, tightly integrating all the other parts of Trac into a flexible and powerful whole.\\r\\n\\r\\nTrac has a built in small and powerful wiki rendering engine. This wiki engine implements an ever growing subset of the commands from other popular Wikis,\\r\\nespecially [http://moinmo.in/ MoinMoin] and [trac:WikiCreole].\\r\\n\\r\\n\\r\\nThis page will give you an in-depth explanation of the wiki markup available anywhere WikiFormatting is allowed.\\r\\n\\r\\nThe ''''Cheat sheet'''' below gives you a quick overview for the most common syntax, each link in the ''''Category'''' column will lead you to the more detailed explanation later in this page.\\r\\n\\r\\nA few other wiki pages present the advanced features of the Trac wiki markup in more depth: \\r\\n - TracLinks covers all the possible ways to refer precisely to any Trac resource or parts thereof,\\r\\n - WikiPageNames talks about the various names a wiki page can take, CamelCase or not\\r\\n - WikiMacros lists the macros available for generating dynamic content,\\r\\n - WikiProcessors and WikiHtml details how parts of the wiki text can be processed in special ways\\r\\n\\r\\n\\r\\n== Cheat sheet ==\\r\\n\\r\\n||= ''''''Category'''''' =||= ''''''Wiki Markup'''''' =||= ''''''Display'''''' =||\\r\\n|-----------------------------------------------------------\\r\\n{{{#!th rowspan=3\\r\\n[#FontStyles Font Styles]\\r\\n}}}\\r\\n|| `''''''bold''''''`, `''''italic''''`, `''''''''''Wikipedia style''''''''''` || \\\\\\r\\n|| ''''''bold'''''', ''''italic'''', ''''''''''Wikipedia style'''''''''' ||\\r\\n|| {{{`monospaced (''''other markup ignored'''')`}}} || \\\\\\r\\n|| `monospaced (''''other markup ignored'''')` ||\\r\\n|| `**bold**`, `//italic//`, `**//!WikiCreole style//**` || \\\\\\r\\n|| **bold**, //italic//, **//!WikiCreole style//** ||\\r\\n|-----------------------------------------------------------\\r\\n||= [#Headings Headings] =||\\\\\\r\\n{{{#!td \\r\\n {{{\\r\\n == Level 2 ==\\r\\n === Level 3 ^([#hn note])^\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em\"\\r\\n== Level 2 ==\\r\\n=== Level 3 ^([#hn note])^\\r\\n}}}\\r\\n|-----------------------------------------------------------\\r\\n||= [#Paragraphs Paragraphs]  =||\\\\\\r\\n{{{#!td\\r\\n {{{\\r\\n First paragraph\\r\\n on multiple lines.\\r\\n\\r\\n Second paragraph.\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td\\r\\nFirst paragraph\\r\\non multiple lines.\\r\\n\\r\\nSecond paragraph.\\r\\n}}}\\r\\n|-----------------------------------------------------------\\r\\n||= [#Lists Lists] =||\\\\\\r\\n{{{#!td\\r\\n {{{\\r\\n * bullets list\\r\\n   on multiple lines\\r\\n   1. nested list\\r\\n     a. different numbering \\r\\n        styles\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n* bullets list\\r\\n  on multiple lines\\r\\n  1. nested list\\r\\n    a. different numbering\\r\\n       styles\\r\\n}}}\\r\\n|-----------------------------------------------------------\\r\\n{{{#!th\\r\\n[#DefinitionLists Definition Lists]\\r\\n}}}\\r\\n{{{#!td\\r\\n {{{\\r\\n  term:: definition on\\r\\n         multiple lines\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n term:: definition on\\r\\n        multiple lines\\r\\n}}}\\r\\n|-----------------------------------------------------------\\r\\n||= [#PreformattedText Preformatted Text] =||\\\\\\r\\n{{{#!td\\r\\n {{{\\r\\n {{{\\r\\n multiple lines, ''''no wiki''''\\r\\n       white space respected\\r\\n }}}\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n {{{\\r\\n multiple lines, ''''no wiki''''\\r\\n       white space respected\\r\\n }}}\\r\\n}}}\\r\\n|-----------------------------------------------------------\\r\\n||= [#Blockquotes Blockquotes] =||\\\\\\r\\n{{{#!td\\r\\n {{{\\r\\n   if there''s some leading\\r\\n   space the text is quoted\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n if there''s some leading\\r\\n space the text is quoted\\r\\n}}}\\r\\n|-----------------------------------------------------------\\r\\n||= [#DiscussionCitations Discussion Citations] =||\\\\\\r\\n{{{#!td\\r\\n {{{\\r\\n >> ... (I said)\\r\\n > (he replied)\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n>>... (I said)\\r\\n> (he replied)\\r\\n}}}\\r\\n|-----------------------------------------------------------\\r\\n||= [#Tables Tables] =||\\\\\\r\\n{{{#!td\\r\\n {{{\\r\\n ||= Table Header =|| Cell ||\\r\\n ||||  (details below)  ||\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n||= Table Header =|| Cell ||\\r\\n||||  (details below)  ||\\r\\n}}}\\r\\n|-----------------------------------------------------------\\r\\n{{{#!th rowspan=2\\r\\n[#Links Links]\\r\\n}}}\\r\\n|| `http://trac.edgewall.org` ||\\\\\\r\\n|| http://trac.edgewall.org ||\\r\\n|| `WikiFormatting (CamelCase)` ||\\\\\\r\\n|| WikiFormatting (CamelCase) ||\\r\\n|-----------------------------------------------------------\\r\\n{{{#!th rowspan=5\\r\\n[#TracLinks TracLinks]\\r\\n}}}\\r\\n|| `wiki:WikiFormatting`, `wiki:\"WikiFormatting\"` ||\\\\\\r\\n|| wiki:WikiFormatting, wiki:\"WikiFormatting\" ||\\r\\n|| `#1 (ticket)`, `[1] (changeset)`, `{1} (report)` ||\\\\\\r\\n|| #1 (ticket), [1] (changeset), {1} (report) ||\\r\\n|| `ticket:1, ticket:1#comment:1` ||\\\\\\r\\n|| ticket:1, ticket:1#comment:1 ||\\r\\n|| `Ticket [ticket:1]`, `[ticket:1 ticket one]` ||\\\\\\r\\n|| Ticket [ticket:1], [ticket:1 ticket one] ||\\r\\n|| `Ticket [[ticket:1]]`, `[[ticket:1|ticket one]]` ||\\\\\\r\\n|| Ticket [[ticket:1]], [[ticket:1|ticket one]] ||\\r\\n|-----------------------------------------------------------\\r\\n{{{#!th rowspan=2 \\r\\n[#SettingAnchors Setting Anchors]\\r\\n}}}\\r\\n|| `[=#point1 (1)] First...` ||\\\\\\r\\n|| [=#point1 (1)] First... ||\\r\\n|| `see [#point1 (1)]` ||\\\\\\r\\n|| see [#point1 (1)] ||\\r\\n|-----------------------------------------------------------\\r\\n{{{#!th rowspan=3\\r\\n[#Escaping Escaping Markup]\\r\\n}}}\\r\\n|| `!'''' doubled quotes` ||\\\\\\r\\n|| !'''' doubled quotes ||\\r\\n|| `!wiki:WikiFormatting`, `!WikiFormatting` ||\\\\\\r\\n|| !wiki:WikiFormatting, !WikiFormatting ||\\r\\n|| {{{`}}}`{{{-}}}`{{{`}}}` triple curly brackets` ||\\\\\\r\\n|| `{{{-}}}` triple curly brackets ||\\r\\n|-----------------------------------------------------------\\r\\n||= [#Images Images] =|| `[[Image(`''''link''''`)]]` || [[Image(htdocs:../common/trac_logo_mini.png)]] ||\\r\\n|-----------------------------------------------------------\\r\\n{{{#!th rowspan=2\\r\\n[#Macros Macros]\\r\\n}}}\\r\\n|| `[[MacroList(*)]]` ||  ''''(short list of all available macros)''''  ||\\r\\n|| `[[Image?]]` ||  ''''(help for the Image macro)''''  ||\\r\\n|-----------------------------------------------------------\\r\\n||= [#Processors Processors] =||\\\\\\r\\n{{{#!td\\r\\n {{{\\r\\n {{{\\r\\n #!div style=\"font-size: 80%\"\\r\\n Code highlighting:\\r\\n   {{{#!python\\r\\n   hello = lambda: \"world\"\\r\\n   }}}\\r\\n }}}\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em\"\\r\\n {{{\\r\\n #!div style=\"font-size: 80%\"\\r\\n Code highlighting:\\r\\n   {{{#!python \\r\\n   hello = lambda: \"world\"\\r\\n   }}}\\r\\n }}}\\r\\n}}}\\r\\n|-----------------------------------------------------------\\r\\n||= [#Comments Comments] =||\\\\\\r\\n{{{#!td\\r\\n {{{\\r\\n {{{#!comment\\r\\n Note to Editors: ...\\r\\n }}}\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em\"\\r\\n {{{#!comment\\r\\n Note to Editors: ...\\r\\n }}}\\r\\n}}}\\r\\n|-----------------------------------------------------------\\r\\n||= [#Miscellaneous Miscellaneous] =||\\\\\\r\\n{{{#!td\\r\\n {{{\\r\\n Line [[br]] break \\r\\n Line \\\\\\\\ break\\r\\n ----\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em\"\\r\\nLine [[br]] break\\r\\nLine \\\\\\\\ break\\r\\n----\\r\\n}}}\\r\\n\\r\\n\\r\\n== Font Styles ==\\r\\n\\r\\nThe Trac wiki supports the following font styles:\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n   * ''''''bold'''''', \\r\\n     '''''' triple quotes !'''''' \\r\\n     can be bold too if prefixed by ! '''''', \\r\\n   * ''''italic''''\\r\\n   * ''''''''''bold italic'''''''''' or ''''italic and\\r\\n     '''''' italic bold '''''' ''''\\r\\n   * __underline__\\r\\n   * {{{monospace}}} or `monospace`\\r\\n     (hence `{{{` or {{{`}}} quoting)\\r\\n   * ~~strike-through~~\\r\\n   * ^superscript^ \\r\\n   * ,,subscript,,\\r\\n   * **also bold**, //italic as well//, \\r\\n     and **'''' bold italic **'''' //(since 0.12)//\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n * ''''''bold'''''', \\r\\n   '''''' triple quotes !'''''' \\r\\n   can be bold too if prefixed by ! '''''', \\r\\n * ''''italic''''\\r\\n * ''''''''''bold italic'''''''''' or ''''italic and\\r\\n   '''''' italic bold '''''' ''''\\r\\n * __underline__\\r\\n * {{{monospace}}} or `monospace`\\r\\n   (hence `{{{` or {{{`}}} quoting)\\r\\n * ~~strike-through~~\\r\\n * ^superscript^ \\r\\n * ,,subscript,,\\r\\n * **also bold**, //italic as well//, \\r\\n   and **'''' bold italic **'''' //(since 0.12)//\\r\\n}}}\\r\\n\\r\\nNotes:\\r\\n * `{{{...}}}` and {{{`...`}}} commands not only select a monospace font, but also treat their content as verbatim text, meaning that no further wiki processing is done on this text.\\r\\n * {{{ ! }}} tells wiki parser to not take the following characters as wiki format, so pay attention to put a space after !, e.g. when ending bold.\\r\\n * all the font styles marks have to be used in opening/closing pairs, \\r\\n   and they must nest properly (in particular, an `''''` italic can''t be paired \\r\\n   with a `//` one, and `''''''` can''t be paired with `**`)\\r\\n\\r\\n\\r\\n== Headings ==\\r\\n\\r\\nYou can create heading by starting a line with one up to six ''''equal'''' characters (\"=\")\\r\\nfollowed by a single space and the headline text. \\r\\n\\r\\n[=#hn]\\r\\nThe headline text can be followed by the same number of \"=\" characters, but this is no longer mandatory.\\r\\n\\r\\nFinally, the heading might optionally be followed by an explicit id. If not, an implicit but nevertheless readable id will be generated.\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  = Heading =\\r\\n  == Subheading\\r\\n  === About ''''this'''' ===\\r\\n  === Explicit id === #using-explicit-id-in-heading\\r\\n  == Subheading #sub2\\r\\n}}}\\r\\n}}}\\r\\n{{{#!td style=\"padding: 1em;\"\\r\\n  {{{\\r\\n  #!div\\r\\n  = Heading =\\r\\n  == Subheading\\r\\n  === About ''''this'''' ===\\r\\n  === Explicit id === #using-explicit-id-in-heading\\r\\n  == Subheading #sub2\\r\\n  }}}\\r\\n}}}\\r\\n\\r\\n== Paragraphs ==\\r\\n\\r\\nA new text paragraph is created whenever two blocks of text are separated by one or more empty lines.\\r\\n\\r\\nA forced line break can also be inserted, using:\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  Line 1[[BR]]Line 2\\r\\n  }}}\\r\\n  {{{\\r\\n  Paragraph\\r\\n  one\\r\\n\\r\\n  Paragraph \\r\\n  two\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n  Line 1[[BR]]Line 2\\r\\n\\r\\n  Paragraph \\r\\n  one\\r\\n\\r\\n  Paragraph \\r\\n  two\\r\\n}}}\\r\\n\\r\\n== Lists ==\\r\\n\\r\\nThe wiki supports both ordered/numbered and unordered lists.\\r\\n\\r\\nExample:\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n   * Item 1\\r\\n     * Item 1.1\\r\\n        * Item 1.1.1   \\r\\n        * Item 1.1.2\\r\\n        * Item 1.1.3\\r\\n     * Item 1.2\\r\\n   * Item 2\\r\\n  - items can start at the beginning of a line\\r\\n    and they can span multiple lines\\r\\n    - be careful though to continue the line \\r\\n    with the appropriate indentation, otherwise\\r\\n  that will start a new paragraph...\\r\\n  \\r\\n   1. Item 1\\r\\n     a. Item 1.a\\r\\n     a. Item 1.b\\r\\n        i. Item 1.b.i\\r\\n        i. Item 1.b.ii\\r\\n   1. Item 2\\r\\n  And numbered lists can also be restarted\\r\\n  with an explicit number:\\r\\n   3. Item 3\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n * Item 1\\r\\n   * Item 1.1\\r\\n      * Item 1.1.1   \\r\\n      * Item 1.1.2\\r\\n      * Item 1.1.3\\r\\n   * Item 1.2\\r\\n * Item 2\\r\\n- items can start at the beginning of a line\\r\\n  and they can span multiple lines\\r\\n  - be careful though to continue the line \\r\\n  with the appropriate indentation, otherwise\\r\\nthat will start a new paragraph...\\r\\n\\r\\n 1. Item 1\\r\\n   a. Item 1.a\\r\\n   a. Item 1.b\\r\\n      i. Item 1.b.i\\r\\n      i. Item 1.b.ii\\r\\n 1. Item 2\\r\\nAnd numbered lists can also be restarted with an explicit number:\\r\\n 3. Item 3\\r\\n}}}\\r\\n\\r\\n\\r\\n== Definition Lists ==\\r\\n\\r\\nThe wiki also supports definition lists.\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n   llama::\\r\\n     some kind of mammal, with hair\\r\\n   ppython::\\r\\n     some kind of reptile, without hair\\r\\n     (can you spot the typo?)\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n llama::\\r\\n   some kind of mammal, with hair\\r\\n ppython::\\r\\n   some kind of reptile, without hair\\r\\n   (can you spot the typo?)\\r\\n}}}\\r\\n\\r\\nNote that you need a space in front of the defined term.\\r\\n\\r\\n\\r\\n== Preformatted Text ==\\r\\n\\r\\nBlock containing preformatted text are suitable for source code snippets, notes and examples. Use three ''''curly braces'''' wrapped around the text to define a block quote. The curly braces need to be on a separate line.\\r\\n  \\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  {{{\\r\\n  def HelloWorld():\\r\\n      print ''''''Hello World''''''\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n  {{{\\r\\n  def HelloWorld():\\r\\n      print ''''''Hello World''''''\\r\\n  }}}\\r\\n}}}\\r\\n\\r\\nNote that this kind of block is also used for selecting lines that should be processed through WikiProcessors.\\r\\n\\r\\n== Blockquotes ==\\r\\n\\r\\nIn order to mark a paragraph as blockquote, indent that paragraph with two spaces.\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n{{{\\r\\nParagraph\\r\\n  This text is a quote from someone else.\\r\\n}}}\\r\\n}}}\\r\\n{{{#!td\\r\\nParagraph\\r\\n  This text is a quote from someone else.\\r\\n}}}\\r\\n\\r\\n== Discussion Citations ==\\r\\n\\r\\nTo delineate a citation in an ongoing discussion thread, such as the ticket comment area, e-mail-like citation marks (\">\", \">>\", etc.) may be used.  \\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  >> Someone''s original text\\r\\n  > Someone else''s reply text\\r\\n  >  - which can be any kind of Wiki markup\\r\\n  My reply text\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n>> Someone''s original text\\r\\n> Someone else''s reply text\\r\\n>  - which can be any kind of Wiki markup\\r\\nMy reply text\\r\\n}}}\\r\\n\\r\\n\\r\\n== Tables ==\\r\\n=== Simple Tables ===\\r\\nSimple tables can be created like this:\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  ||Cell 1||Cell 2||Cell 3||\\r\\n  ||Cell 4||Cell 5||Cell 6||\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding: 2em;\"\\r\\n||Cell 1||Cell 2||Cell 3||\\r\\n||Cell 4||Cell 5||Cell 6||\\r\\n}}}\\r\\n\\r\\nCell headings can be specified by wrapping the content in a pair of ''='' characters.\\r\\nNote that the ''='' characters have to stick to the cell separators, like this:\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  ||        ||= stable =||= latest =||\\r\\n  ||= 0.10 =||  0.10.5  || 0.10.6dev||\\r\\n  ||= 0.11 =||  0.11.6  || 0.11.7dev||\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding: 2em;\"\\r\\n||        ||= stable =||= latest =||\\r\\n||= 0.10 =||  0.10.5  || 0.10.6dev||\\r\\n||= 0.11 =||  0.11.6  || 0.11.7dev||\\r\\n}}}\\r\\n\\r\\nFinally, specifying an empty cell means that the next non empty cell will span the empty cells. For example:\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  || 1 || 2 || 3 ||\\r\\n  |||| 1-2 || 3 ||\\r\\n  || 1 |||| 2-3 ||\\r\\n  |||||| 1-2-3 ||\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding: 2em;\"\\r\\n|| 1 || 2 || 3 ||\\r\\n|||| 1-2 || 3 ||\\r\\n|| 1 |||| 2-3 ||\\r\\n|||||| 1-2-3 ||\\r\\n}}}\\r\\n\\r\\nNote that if the content of a cell \"sticks\" to one side of the cell and only one, then the text will be aligned on that side. Example:\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  ||=Text =||= Numbers =||\\r\\n  ||left align    ||        1.0||\\r\\n  ||  center      ||        4.5||\\r\\n  ||      right align||     4.5||\\r\\n  || default alignment ||   2.5||\\r\\n  ||default||         2.5||\\r\\n  ||  default ||      2.5||\\r\\n  || default ||       2.5||\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding: 2em;\"\\r\\n||=Text =||= Numbers =||\\r\\n||left align    ||        1.0||\\r\\n||  center      ||        4.5||\\r\\n||      right align||     4.5||\\r\\n|| default alignment ||   2.5||\\r\\n||default||         2.5||\\r\\n||  default ||      2.5||\\r\\n|| default ||       2.5||\\r\\n}}}\\r\\n\\r\\nIf contrary to the example above, the cells in your table contain more text, it might be convenient to spread a table row over multiple lines of markup. The `\\\\` character placed at the end of a line after a cell separator tells Trac to not start a new row for the cells on the next line.\\r\\n\\r\\n||= Wiki Markup =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  || this is column 1 [http://trac.edgewall.org/newticket new ticket] || \\\\\\r\\n  || this is column 2 [http://trac.edgewall.org/roadmap the road ahead] || \\\\\\r\\n  || that''s column 3 and last one ||\\r\\n  }}}\\r\\n}}}\\r\\n|-------------\\r\\n||= Display =||\\r\\n{{{#!td style=\"padding: 2em;\"\\r\\n|| this is column 1 [http://trac.edgewall.org/newticket new ticket] || \\\\\\r\\n|| this is column 2 [http://trac.edgewall.org/roadmap the road ahead] || \\\\\\r\\n|| that''s column 3 and last one ||\\r\\n}}}\\r\\n\\r\\n=== Complex Tables ===\\r\\n\\r\\nIf the possibilities offered by the simple \"pipe\"-based markup for tables described above are not enough for your needs, you can create more elaborated tables by using [#Processors-example-tables WikiProcessor based tables].\\r\\n\\r\\n\\r\\n== Links ==\\r\\n\\r\\nHyperlinks are automatically created for WikiPageNames and URLs. !WikiPageLinks can be disabled by prepending an exclamation mark \"!\" character, such as {{{!WikiPageLink}}}.\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  TitleIndex, http://www.edgewall.com/, !NotAlink\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\nTitleIndex, http://www.edgewall.com/, !NotAlink\\r\\n}}}\\r\\n\\r\\nLinks can be given a more descriptive title by writing the link followed by a space and a title and all this inside square brackets. \\r\\nIf the descriptive title is omitted, then the explicit prefix is discarded, unless the link is an external link. This can be useful for wiki pages not adhering to the WikiPageNames convention.\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n   * [http://www.edgewall.com Edgewall Software]\\r\\n   * [wiki:TitleIndex Title Index] \\r\\n   * [wiki:TitleIndex] \\r\\n   * [wiki:ISO9000]\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n   * [http://www.edgewall.com Edgewall Software]\\r\\n   * [wiki:TitleIndex Title Index] \\r\\n   * [wiki:TitleIndex] \\r\\n   * [wiki:ISO9000]\\r\\n}}}\\r\\n\\r\\nFollowing the [trac:WikiCreole] trend, the descriptive title can also be specified by writing the link followed by a pipe (''|'') and a title and all this inside //double// square brackets. \\r\\n\\r\\n{{{#!td\\r\\n  {{{\\r\\n   * [[http://www.edgewall.com|Edgewall Software]]\\r\\n   * [[wiki:TitleIndex|Title Index]]\\r\\n     or even [[TitleIndex|Title Index]]\\r\\n   * [[wiki:TitleIndex]]\\r\\n     '''''' but not ![[TitleIndex]]! ''''''\\r\\n   * [[ISO9000]]\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n   * [[http://www.edgewall.com|Edgewall Software]]\\r\\n   * [[wiki:TitleIndex|Title Index]]\\r\\n     or even [[TitleIndex|Title Index]]\\r\\n   * [[wiki:TitleIndex]]\\r\\n     '''''' but not ![[TitleIndex]]! ''''''\\r\\n   * [[ISO9000]]\\r\\n}}}\\r\\n\\r\\n''''''Note'''''': the [trac:WikiCreole] style for links is quick to type and\\r\\ncertainly looks familiar as it''s the one used on Wikipedia and in many\\r\\nother wikis. Unfortunately it conflicts with the syntax for [#Macros macros].\\r\\nSo in the rare case when you need to refer to a page which is named after\\r\\na macro (typical examples being TitleIndex, InterTrac and InterWiki), \\r\\nby writing `[[TitleIndex]]` you will actually call the macro instead of linking\\r\\nto the page.\\r\\n\\r\\n== Trac Links ==\\r\\n\\r\\nWiki pages can link directly to other parts of the Trac system. Pages can refer to tickets, reports, changesets, milestones, source files and other Wiki pages using the following notations:\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n   * Tickets: #1 or ticket:1\\r\\n   * Reports: {1} or report:1\\r\\n   * Changesets: r1, [1] or changeset:1\\r\\n   * ...\\r\\n   * targeting other Trac instances, \\r\\n     so called InterTrac links:\\r\\n     - Tickets: #Trac1 or Trac:ticket:1\\r\\n     - Changesets: [Trac1] or Trac:changeset:1\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n * Tickets: #1 or ticket:1\\r\\n * Reports: {1} or report:1\\r\\n * Changesets: r1, [1] or changeset:1\\r\\n * ... \\r\\n * targeting other Trac instances, \\r\\n   so called InterTrac links:\\r\\n   - Tickets: #Trac1 or Trac:ticket:1\\r\\n   - Changesets: [Trac1] or Trac:changeset:1\\r\\n}}}\\r\\n\\r\\nThere are many more flavors of Trac links, see TracLinks for more in-depth information and a reference for all the default link resolvers.\\r\\n\\r\\n\\r\\n== Setting Anchors ==\\r\\n\\r\\nAn anchor, or more correctly speaking, an [http://www.w3.org/TR/REC-html40/struct/links.html#h-12.2.1 anchor name] can be added explicitly at any place in the Wiki page, in order to uniquely identify a position in the document:\\r\\n\\r\\n{{{\\r\\n[=#point1]\\r\\n}}}\\r\\n\\r\\nThis syntax was chosen to match the format for explicitly naming the header id [#Headings documented above]. For example:\\r\\n{{{\\r\\n== Long title == #title\\r\\n}}}\\r\\n\\r\\nIt''s also very close to the syntax for the corresponding link to that anchor:\\r\\n{{{\\r\\n[#point1]\\r\\n}}}\\r\\n\\r\\nOptionally, a label can be given to the anchor:\\r\\n{{{\\r\\n[[=#point1 ''''''Point 1'''''']]\\r\\n}}}\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n|----------------------------------\\r\\n{{{#!td\\r\\n  {{{\\r\\n  [#point2 jump to the second point]\\r\\n\\r\\n  ...\\r\\n\\r\\n  Point2:  [=#point2] Jump here\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n  [#point2 jump to the second point]\\r\\n\\r\\n  ...\\r\\n\\r\\n  Point2:  [=#point2] Jump here\\r\\n}}}\\r\\n\\r\\nFor more complex anchors (e.g. when a custom title is wanted), one can use the Span macro, e.g. `[[span(id=point2, class=wikianchor, title=Point 2, ^(2)^)]]`.\\r\\n\\r\\n\\r\\n== Escaping Links, WikiPageNames and other Markup == #Escaping\\r\\n\\r\\nYou may avoid making hyperlinks out of TracLinks by preceding an expression with a single \"!\" (exclamation mark).\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n   !NoHyperLink\\r\\n   !#42 is not a link\\r\\n  }}}\\r\\n  {{{\\r\\nVarious forms of escaping for list markup:\\r\\n `-` escaped minus sign \\\\\\\\\\r\\n ``1. escaped number  \\\\\\\\\\r\\n {{{*}}} escaped asterisk sign\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n !NoHyperLink\\r\\n !#42 is not a link\\r\\n\\r\\nVarious forms of escaping for list markup:\\r\\n `-` escaped minus sign \\\\\\\\\\r\\n ``1. escaped number  \\\\\\\\\\r\\n {{{*}}} escaped asterisk sign\\r\\n}}}\\r\\n\\r\\n== Images ==\\r\\n\\r\\nUrls ending with `.png`, `.gif` or `.jpg` are no longer automatically interpreted as image links, and converted to `<img>` tags.\\r\\n\\r\\nYou now have to use the ![[Image]] macro. The simplest way to include an image is to upload it as attachment to the current page, and put the filename in a macro call like `[[Image(picture.gif)]]`.\\r\\n\\r\\nIn addition to the current page, it is possible to refer to other resources:\\r\\n * `[[Image(wiki:WikiFormatting:picture.gif)]]` (referring to attachment on another page)\\r\\n * `[[Image(ticket:1:picture.gif)]]` (file attached to a ticket)\\r\\n * `[[Image(htdocs:picture.gif)]]` (referring to a file inside the [TracEnvironment environment] `htdocs` directory)\\r\\n * `[[Image(source:/trunk/trac/htdocs/trac_logo_mini.png)]]` (a file in repository)\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  [[Image(htdocs:../common/trac_logo_mini.png)]]\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n[[Image(htdocs:../common/trac_logo_mini.png)]]\\r\\n}}}\\r\\n\\r\\nSee WikiMacros for further documentation on the `[[Image()]]` macro, which has several useful options (`title=`, `link=`, etc.)\\r\\n\\r\\n\\r\\n== Macros ==\\r\\n\\r\\nMacros are ''''custom functions'''' to insert dynamic content in a page.\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  [[RecentChanges(Trac,3)]]\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em\"\\r\\n[[RecentChanges(Trac,3)]]\\r\\n}}}\\r\\n\\r\\nSee WikiMacros for more information, and a list of installed macros.\\r\\n\\r\\nThe detailed help for a specific macro can also be obtained more directly by appending a \"?\" to the macro name.\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  [[MacroList?]]\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em\"\\r\\n[[MacroList?]]\\r\\n}}}\\r\\n\\r\\n\\r\\n== Processors ==\\r\\n\\r\\nTrac supports alternative markup formats using WikiProcessors. For example, processors are used to write pages in \\r\\n[wiki:WikiRestructuredText reStructuredText] or [wiki:WikiHtml HTML]. \\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n|--------------------------------------------------------\\r\\n{{{#!td align=\"center\" colspan=2 style=\"border: 0px; font-size: 90%\"\\r\\n\\r\\n   [=#Processors-example-html Example 1:] HTML\\r\\n\\r\\n}}}\\r\\n|--------------------------------------------------------\\r\\n{{{#!td style=\"border: 0px\"\\r\\n  {{{\\r\\n  {{{\\r\\n  #!html\\r\\n  <h1 style=\"text-align: right; color: blue\">\\r\\n   HTML Test\\r\\n  </h1>\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td valign=\"top\"  style=\"border: 0px\"\\r\\n\\r\\n{{{\\r\\n#!html\\r\\n<h1 style=\"text-align: right; color: blue\">HTML Test</h1>\\r\\n}}}\\r\\n\\r\\n}}}\\r\\n|--------------------------------------------------------\\r\\n{{{#!td align=\"center\" colspan=2 style=\"border: 0px; font-size: 90%\"\\r\\n\\r\\n   [=#Processors-example-highlight Example 2:] Code Highlighting\\r\\n\\r\\n}}}\\r\\n|--------------------------------------------------------\\r\\n{{{#!td style=\"border: 0px\"\\r\\n  {{{\\r\\n  {{{\\r\\n  #!python\\r\\n  class Test:\\r\\n  \\r\\n      def __init__(self):\\r\\n          print \"Hello World\"\\r\\n  if __name__ == ''__main__'':\\r\\n     Test()\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{\\r\\n#!td valign=\"top\"  style=\"border: 0px\"\\r\\n\\r\\n{{{\\r\\n#!python\\r\\nclass Test:\\r\\n    def __init__(self):\\r\\n        print \"Hello World\"\\r\\nif __name__ == ''__main__'':\\r\\n   Test()\\r\\n}}}\\r\\n\\r\\n}}}\\r\\n|--------------------------------------------------------\\r\\n{{{#!td align=\"center\" colspan=2 style=\"border: 0px; font-size: 90%\"\\r\\n\\r\\n       [=#Processors-example-tables Example 3:] Complex Tables\\r\\n\\r\\n}}}\\r\\n|--------------------------------------------------------\\r\\n{{{#!td style=\"border: 0px\"\\r\\n  {{{\\r\\n  {{{#!th rowspan=4 align=justify\\r\\n  With the `#td` and `#th` processors,\\r\\n  table cells can contain any content:\\r\\n  }}}\\r\\n  |----------------\\r\\n  {{{#!td\\r\\n    - lists\\r\\n    - embedded tables\\r\\n    - simple multiline content\\r\\n  }}}\\r\\n  |----------------\\r\\n  {{{#!td\\r\\n  As processors can be easily nested, \\r\\n  so can be tables:\\r\\n    {{{#!th\\r\\n    Example:\\r\\n    }}}\\r\\n    {{{#!td style=\"background: #eef\"\\r\\n    || must be at the third level now... ||\\r\\n    }}}\\r\\n  }}}\\r\\n  |----------------\\r\\n  {{{#!td\\r\\n  Even when you don''t have complex markup,\\r\\n  this form of table cells can be convenient\\r\\n  to write content on multiple lines.\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{\\r\\n#!td  valign=\"top\"  style=\"border: 0px\"\\r\\n\\r\\n  {{{#!th rowspan=4 align=justify\\r\\n  With the `#td` and `#th` processors,\\r\\n  table cells can contain any content:\\r\\n  }}}\\r\\n  |----------------\\r\\n  {{{#!td\\r\\n    - lists\\r\\n    - embedded tables\\r\\n    - simple multiline content\\r\\n  }}}\\r\\n  |----------------\\r\\n  {{{#!td\\r\\n  As processors can be easily nested, \\r\\n  so can be tables:\\r\\n    {{{#!th\\r\\n    Example:\\r\\n    }}}\\r\\n    {{{#!td style=\"background: #eef\"\\r\\n    || must be at the third level now... ||\\r\\n    }}}\\r\\n  }}}\\r\\n  |----------------\\r\\n  {{{#!td\\r\\n  Even when you don''t have complex markup,\\r\\n  this form of table cells can be convenient\\r\\n  to write content on multiple lines.\\r\\n  }}}\\r\\n\\r\\n}}}\\r\\n\\r\\nSee WikiProcessors for more information.\\r\\n\\r\\n\\r\\n== Comments ==\\r\\n\\r\\nComments can be added to the plain text. These will not be rendered and will not display in any other format than plain text.\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  Nothing to\\r\\n  {{{\\r\\n  #!comment\\r\\n  Your comment for editors here\\r\\n  }}}\\r\\n  see ;-)\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n  Nothing to\\r\\n  {{{\\r\\n  #!comment\\r\\n  Your comment for editors here\\r\\n  }}}\\r\\n  see ;-)\\r\\n}}}\\r\\n\\r\\n== Miscellaneous ==\\r\\n\\r\\nAn horizontal line can be used to separated different parts of your page:\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  Four or more dashes will be replaced \\r\\n  by an horizontal line (<HR>)\\r\\n  ----\\r\\n  See?\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\nFour or more dashes will be replaced\\r\\nby an horizontal line (<HR>)\\r\\n----\\r\\nSee?\\r\\n}}}\\r\\n|----------------------------------\\r\\n{{{#!td\\r\\n  {{{\\r\\n  \"macro\" style [[br]] line break\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n\"macro\" style [[br]] line break\\r\\n}}}\\r\\n|----------------------------------\\r\\n{{{#!td\\r\\n  {{{\\r\\n  !WikiCreole style \\\\\\\\ line\\\\\\\\break\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n!WikiCreole style \\\\\\\\ line\\\\\\\\break\\r\\n}}}\\r\\n\\r\\n', NULL, NULL),\n('InterMapTxt', 1, 1362994208257901, 'trac', '127.0.0.1', '= InterMapTxt =\\r\\n\\r\\n== This is the place for defining InterWiki prefixes ==\\r\\n\\r\\nThis page was modelled after the MeatBall:InterMapTxt page.\\r\\nIn addition, an optional comment is allowed after the mapping.\\r\\n\\r\\n\\r\\nThis page is interpreted in a special way by Trac, in order to support\\r\\n!InterWiki links in a flexible and dynamic way.\\r\\n\\r\\nThe code block after the first line separator in this page\\r\\nwill be interpreted as a list of !InterWiki specifications:\\r\\n{{{\\r\\nprefix <space> URL [<space> # comment]\\r\\n}}}\\r\\n\\r\\nBy using `$1`, `$2`, etc. within the URL, it is possible to create \\r\\nInterWiki links which support multiple arguments, e.g. Trac:ticket:40.\\r\\nThe URL itself can be optionally followed by a comment, \\r\\nwhich will subsequently be used for decorating the links \\r\\nusing that prefix.\\r\\n\\r\\nNew !InterWiki links can be created by adding to that list, in real time.\\r\\nNote however that ''''deletions'''' are also taken into account immediately,\\r\\nso it may be better to use comments for disabling prefixes.\\r\\n\\r\\nAlso note that !InterWiki prefixes are case insensitive.\\r\\n\\r\\n\\r\\n== List of Active Prefixes ==\\r\\n\\r\\n[[InterWiki]]\\r\\n\\r\\n\\r\\n----\\r\\n\\r\\n== Prefix Definitions ==\\r\\n\\r\\n{{{\\r\\nPEP     http://www.python.org/peps/pep-$1.html    # Python Enhancement Proposal \\r\\nPythonBug    http://bugs.python.org/issue$1       # Python Issue #$1\\r\\nPython-issue http://bugs.python.org/issue$1       # Python Issue #$1\\r\\n\\r\\nTrac-ML  http://thread.gmane.org/gmane.comp.version-control.subversion.trac.general/ # Message $1 in Trac Mailing List\\r\\ntrac-dev http://thread.gmane.org/gmane.comp.version-control.subversion.trac.devel/   # Message $1 in Trac Development Mailing List\\r\\n\\r\\nMercurial http://www.selenic.com/mercurial/wiki/index.cgi/ # the wiki for the Mercurial distributed SCM\\r\\n\\r\\nRFC       http://tools.ietf.org/html/rfc$1          # IETF''s RFC $1\\r\\nISO       http://en.wikipedia.org/wiki/ISO_         # ISO Standard $1 in Wikipedia\\r\\nkb        http://support.microsoft.com/kb/$1/en-us/ # Article $1 in Microsoft''s Knowledge Base\\r\\n\\r\\nchromium-issue  http://code.google.com/p/chromium/issues/detail?id=\\r\\n\\r\\nDjango      http://code.djangoproject.com/intertrac/ # Django''s Trac\\r\\n\\r\\nCreoleWiki   http://wikicreole.org/wiki/\\r\\nCreole1Wiki  http://wikicreole.org/wiki/\\r\\nCreole2Wiki  http://wiki.wikicreole.org/\\r\\n\\r\\nMediaWiki    http://www.mediawiki.org/wiki/\\r\\n\\r\\nSO  http://stackoverflow.com/questions/ # Question $1 in StackOverflow\\r\\n\\r\\nTransifex https://www.transifex.com/projects/p/trac/\\r\\n\\r\\nkwquery      /query?group=status&keywords=~  # Custom query for tickets matching keyword $1\\r\\n\\r\\n#\\r\\n# A arbitrary pick of InterWiki prefixes...\\r\\n#\\r\\nAcronym          http://www.acronymfinder.com/af-query.asp?String=exact&Acronym=\\r\\nC2find           http://c2.com/cgi/wiki?FindPage&value=\\r\\nCache            http://www.google.com/search?q=cache:\\r\\nCPAN             http://search.cpan.org/perldoc?\\r\\nDebianBug        http://bugs.debian.org/\\r\\nDebianPackage    http://packages.debian.org/\\r\\nDebianPTS        http://packages.qa.debian.org/\\r\\nDictionary       http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=\\r\\nGoogle           http://www.google.com/search?q=\\r\\nlmgtfy           http://lmgtfy.com/?q= # Well, just search for \"$1\", follow the link to see how to do it...\\r\\nGoogleGroups     http://groups.google.com/group/$1/msg/$2        # Message $2 in $1 Google Group\\r\\ngdiscussion      https://groups.google.com/d/topic/$1/$2/discussion # Discussion $2 in $1 Google \\r\\ngmessage         https://groups.google.com/d/msg/$1/$2 # Message $2 in $1 Google Group\\r\\nJargonFile       http://downlode.org/perl/jargon-redirect.cgi?term=\\r\\nMeatBall         http://www.usemod.com/cgi-bin/mb.pl?\\r\\nMetaWiki         http://sunir.org/apps/meta.pl?\\r\\nMetaWikiPedia    http://meta.wikipedia.org/wiki/\\r\\nMoinMoin         http://moinmo.in/\\r\\nTracHacks        http://trac-hacks.org/wiki/\\r\\nOSM              http://www.openstreetmap.org/wiki/\\r\\nWhoIs            http://www.whois.sc/\\r\\nWhy              http://clublet.com/c/c/why?\\r\\nc2Wiki           http://c2.com/cgi/wiki?\\r\\nWikiPedia        http://en.wikipedia.org/wiki/\\r\\n}}}\\r\\n\\r\\n\\r\\n----\\r\\nSee also: InterWiki, InterTrac', NULL, NULL),\n('PageTemplates', 1, 1362994208259845, 'trac', '127.0.0.1', '= Wiki Page Templates = \\r\\n\\r\\n  ''''(since [http://trac.edgewall.org/milestone/0.11 0.11])''''\\r\\n\\r\\nThe default content for a new wiki page can be chosen from a list of page templates. \\r\\n\\r\\nThat list is generated from all the wiki pages having a name starting with ''''PageTemplates/''''.\\r\\nThe initial content of a new page will simply be the content of the chosen template page, or a blank page if the special ''''(blank page)'''' entry is selected. When there are no wiki pages with the ''''PageTemplates/'''' prefix, the initial content will always be the blank page and the list selector will not be shown (i.e. this matches the behavior we had up to now).\\r\\n\\r\\nTo create a new template, simply create a new page having a name starting with ''''PageTemplates/''''.\\r\\n\\r\\n(Hint: one could even create a ''''!PageTemplates/Template'''' for facilitating the creation of new templates!)\\r\\n\\r\\nAfter the first template has been created, a drop-down selection box will automatically appear on any new wiki pages that are created.  By default it is located on the right side of the ''Create this page'' button. The default selection will be ''''blank page'''', or ''''!DefaultPage'''' if ''''!PageTemplates/DefaultPage'''' exists.\\r\\n\\r\\nAvailable templates: \\r\\n[[TitleIndex(PageTemplates/)]]\\r\\n----\\r\\nSee also: TracWiki\\r\\n', NULL, NULL),\n('CamelCase', 1, 1362994208261554, 'trac', '127.0.0.1', '= !CamelCase =\\r\\nNew words created by smashing together capitalized words.\\r\\n\\r\\nCamelCase is the original wiki convention for creating hyperlinks, with the additional requirement that the capitals are followed by a lower-case letter; hence “AlabamA” and “ABc” will not be links.\\r\\n\\r\\n== Customizing the Wiki behavior ==\\r\\n\\r\\nSome people dislike linking by CamelCase.  While Trac remains faithful to the original Wiki style, it provides a number of ways to accomodate users with different preferences:\\r\\n * There''s an option (`ignore_missing_pages` in the [wiki:TracIni#wiki-section \"[wiki]\"] section of TracIni) to simply ignore links to missing pages when the link is written using the CamelCase style, instead of that word being replaced by a gray link followed by a question mark.[[BR]]\\r\\n   That can be useful when CamelCase style is used to name code artifacts like class names and there''s no corresponding page for them.\\r\\n * There''s an option (`split_page_names` in the  [wiki:TracIni#wiki-section \"[wiki]\"] section of TracIni) to automatically insert space characters between the words of a CamelCase link when rendering the link.\\r\\n * Creation of explicit Wiki links is also easy, see WikiPageNames for details.\\r\\n * In addition, Wiki formatting can be disabled completely in some places (e.g. when rendering commit log messages). See `wiki_format_messages` in the [wiki:TracIni#changeset-section \"[changeset]\"] section of TracIni.\\r\\n\\r\\nSee TracIni for more information on the available options.\\r\\n\\r\\n== More information on !CamelCase ==\\r\\n\\r\\n * http://c2.com/cgi/wiki?WikiCase\\r\\n * http://en.wikipedia.org/wiki/CamelCase\\r\\n\\r\\n----\\r\\nSee also: WikiPageNames, WikiNewPage, WikiFormatting, TracWiki\\r\\n', NULL, NULL),\n('TracInterfaceCustomization', 1, 1362994208263524, 'trac', '127.0.0.1', '= Customizing the Trac Interface =\\r\\n[[TracGuideToc]]\\r\\n[[PageOutline]]\\r\\n\\r\\n== Introduction ==\\r\\nThis page is meant to give users suggestions on how they can customize the look of Trac.  Topics on this page cover editing the HTML templates and CSS files, but not the program code itself.  The topics are intended to show users how they can modify the look of Trac to meet their specific needs.  Suggestions for changes to Trac''s interface applicable to all users should be filed as tickets, not listed on this page.\\r\\n\\r\\n== Project Logo and Icon ==\\r\\nThe easiest parts of the Trac interface to customize are the logo and the site icon.  Both of these can be configured with settings in [wiki:TracIni trac.ini].\\r\\n\\r\\nThe logo or icon image should be put in a folder named \"htdocs\" in your project''s environment folder.  (''''Note: in projects created with a Trac version prior to 0.9 you will need to create this folder'''')\\r\\n\\r\\n ''''Note: you can actually put the logo and icon anywhere on your server (as long as it''s accessible through the web server), and use their absolute or server-relative URLs in the configuration.''''\\r\\n\\r\\nNow configure the appropriate section of your [wiki:TracIni trac.ini]:\\r\\n\\r\\n=== Logo ===\\r\\nChange the `src` setting to `site/` followed by the name of your image file.  The `width` and `height` settings should be modified to match your image''s dimensions (the Trac chrome handler uses \"`site/`\" for files within the project directory `htdocs`, and \"`common/`\" for the common `htdocs` directory belonging to a Trac installation). Note that ''site/'' is not a placeholder for your project name, it is the actual prefix that should be used (literally). For example, if your project is named ''sandbox'', and the image file is ''red_logo.gif'' then the ''src'' setting would be ''site/red_logo.gif'', not ''sandbox/red_logo.gif''.\\r\\n\\r\\n{{{\\r\\n[header_logo]\\r\\nsrc = site/my_logo.gif\\r\\nalt = My Project\\r\\nwidth = 300\\r\\nheight = 100\\r\\n}}}\\r\\n\\r\\n=== Icon ===\\r\\nIcons should be a 32x32 image in `.gif` or `.ico` format.  Change the `icon` setting to `site/` followed by the name of your icon file.  Icons will typically be displayed by your web browser next to the site''s URL and in the `Bookmarks` menu.\\r\\n\\r\\n{{{\\r\\n[project]\\r\\nicon = site/my_icon.ico\\r\\n}}}\\r\\n\\r\\nNote though that this icon is ignored by Internet Explorer, which only accepts a file named ``favicon.ico`` at the root of the host. To make the project icon work in both IE and other browsers, you can store the icon in the document root of the host, and reference it from ``trac.ini`` as follows:\\r\\n\\r\\n{{{\\r\\n[project]\\r\\nicon = /favicon.ico\\r\\n}}}\\r\\n\\r\\nShould your browser have issues with your favicon showing up in the address bar, you may put a \"?\" (less the quotation marks) after your favicon file extension. \\r\\n\\r\\n{{{\\r\\n[project]\\r\\nicon = /favicon.ico?\\r\\n}}}\\r\\n\\r\\n== Custom Navigation Entries ==\\r\\nThe new [mainnav] and [metanav] can now be used to customize the text and link used for the navigation items, or even to disable them (but not for adding new ones).\\r\\n\\r\\nIn the following example, we rename the link to the Wiki start \"Home\", and hide the \"!Help/Guide\". We also make the \"View Tickets\" entry link to a specific report .\\r\\n{{{\\r\\n[mainnav]\\r\\nwiki.label = Home\\r\\ntickets.href = /report/24\\r\\n\\r\\n[metanav]\\r\\nhelp = disabled\\r\\n}}}\\r\\n\\r\\nSee also TracNavigation for a more detailed explanation of the mainnav and metanav terms.\\r\\n\\r\\n== Site Appearance == #SiteAppearance\\r\\n\\r\\nTrac is using [http://genshi.edgewall.org Genshi] as the templating engine. Documentation is yet to be written, in the meantime the following tip should work.\\r\\n\\r\\nSay you want to add a link to a custom stylesheet, and then your own\\r\\nheader and footer. Save the following content as `site.html` inside your projects `templates/` directory (each Trac project can have their own `site.html`), e.g. {{{/path/to/env/templates/site.html}}}:\\r\\n\\r\\n{{{\\r\\n#!xml\\r\\n<html xmlns=\"http://www.w3.org/1999/xhtml\"\\r\\n      xmlns:py=\"http://genshi.edgewall.org/\"\\r\\n      py:strip=\"\">\\r\\n\\r\\n  <!--! Add site-specific style sheet -->\\r\\n  <head py:match=\"head\" py:attrs=\"select(''@*'')\">\\r\\n    ${select(''*|comment()|text()'')}\\r\\n    <link rel=\"stylesheet\" type=\"text/css\"\\r\\n          href=\"${href.chrome(''site/style.css'')}\" />\\r\\n  </head>\\r\\n\\r\\n  <body py:match=\"body\" py:attrs=\"select(''@*'')\">\\r\\n    <!--! Add site-specific header -->\\r\\n    <div id=\"siteheader\">\\r\\n      <!--! Place your header content here... -->\\r\\n    </div>\\r\\n\\r\\n    ${select(''*|text()'')}\\r\\n\\r\\n    <!--! Add site-specific footer -->\\r\\n    <div id=\"sitefooter\">\\r\\n      <!--! Place your footer content here... -->\\r\\n    </div>\\r\\n  </body>\\r\\n</html>\\r\\n}}}\\r\\n\\r\\nThose who are familiar with XSLT may notice that Genshi templates bear some similarities. However, there are some Trac specific features - for example `${href.chrome(''site/style.css'')}` attribute references a CSS file placed into environment''s `htdocs/` directory. In a similar fashion `${chrome.htdocs_location}` is used to specify the common `htdocs/` directory belonging to a Trac installation. That latter location can however be overriden using the [[TracIni#trac-config|[trac] htdocs_location]] configuration setting.\\r\\n\\r\\n`site.html` is one file to contain all your modifications. It usually works using the `py:match` directive (element or attribute), and it allows you to modify the page as it renders - the matches hook onto specific sections depending on what it tries to find\\r\\nand modify them.\\r\\nSee [http://groups.google.com/group/trac-users/browse_thread/thread/70487fb2c406c937/ this thread] for a detailed explanation of the above example `site.html`.\\r\\nA `site.html` can contain any number of such `py:match` sections for whatever you need to modify. This is all Genshi, so the [http://genshi.edgewall.org/wiki/Documentation/xml-templates.html docs on the exact syntax] can be found there.\\r\\n\\r\\n\\r\\nExample snippet of adding introduction text to the new ticket form (but not shown during preview):\\r\\n\\r\\n{{{#!xml\\r\\n<form py:match=\"div[@id=''content'' and @class=''ticket'']/form\" py:attrs=\"select(''@*'')\">\\r\\n  <py:if test=\"req.environ[''PATH_INFO''] == ''/newticket'' and (not ''preview'' in req.args)\">\\r\\n    <p>Please make sure to search for existing tickets before reporting a new one!</p>\\r\\n  </py:if>\\r\\n  ${select(''*'')} \\r\\n</form>\\r\\n}}}\\r\\n\\r\\nThis example illustrates a technique of using `req.environ[''PATH_INFO'']` to limit scope of changes to one view only. For instance, to make changes in `site.html` only for timeline and avoid modifying other sections - use  `req.environ[''PATH_INFO''] == ''/timeline''` condition in `<py:if>` test.\\r\\n\\r\\nMore examples snippets for `site.html` can be found at [trac:wiki:CookBook/SiteHtml CookBook/SiteHtml].\\r\\n\\r\\nExample snippets for `style.css` can be found at [trac:wiki:CookBook/SiteStyleCss CookBook/SiteStyleCss].\\r\\n\\r\\nIf the environment is upgraded from 0.10 and a `site_newticket.cs` file already exists, it can actually be loaded by using a workaround - providing it contains no ClearSilver processing. In addition, as only one element can be imported, the content needs some sort of wrapper such as a `<div>` block or other similar parent container. The XInclude namespace must be specified to allow includes, but that can be moved to document root along with the others:\\r\\n{{{\\r\\n#!xml\\r\\n<form py:match=\"div[@id=''content'' and @class=''ticket'']/form\" py:attrs=\"select(''@*'')\"\\r\\n        xmlns:xi=\"http://www.w3.org/2001/XInclude\">\\r\\n  <py:if test=\"req.environ[''PATH_INFO''] == ''/newticket'' and (not ''preview'' in req.args)\"> \\r\\n    <xi:include href=\"site_newticket.cs\"><xi:fallback /></xi:include>\\r\\n  </py:if>\\r\\n  ${select(''*'')} \\r\\n</form>\\r\\n}}}\\r\\n\\r\\nAlso note that the `site.html` (despite its name) can be put in a common templates directory - see the [[TracIni#inherit-section|[inherit] templates_dir]] option. This could provide easier maintainence (and a migration path from 0.10 for larger installations) as one new global `site.html` file can be made to include any existing header, footer and newticket snippets.\\r\\n\\r\\n== Project List == #ProjectList\\r\\n\\r\\nYou can use a custom Genshi template to display the list of projects if you are using Trac with multiple projects.  \\r\\n\\r\\nThe following is the basic template used by Trac to display a list of links to the projects.  For projects that could not be loaded it displays an error message. You can use this as a starting point for your own index template.\\r\\n\\r\\n{{{\\r\\n#!text/html\\r\\n<!DOCTYPE html\\r\\n    PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\\r\\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\\r\\n<html xmlns=\"http://www.w3.org/1999/xhtml\"\\r\\n      xmlns:py=\"http://genshi.edgewall.org/\"\\r\\n      xmlns:xi=\"http://www.w3.org/2001/XInclude\">\\r\\n  <head>\\r\\n    <title>Available Projects</title>\\r\\n  </head>\\r\\n  <body>\\r\\n    <h1>Available Projects</h1>\\r\\n    <ul>\\r\\n      <li py:for=\"project in projects\" py:choose=\"\">\\r\\n        <a py:when=\"project.href\" href=\"$project.href\"\\r\\n           title=\"$project.description\">$project.name</a>\\r\\n        <py:otherwise>\\r\\n          <small>$project.name: <em>Error</em> <br /> ($project.description)</small>\\r\\n        </py:otherwise>\\r\\n      </li>\\r\\n    </ul>\\r\\n  </body>\\r\\n</html>\\r\\n}}}\\r\\n\\r\\nOnce you''ve created your custom template you will need to configure the webserver to tell Trac where the template is located (pls verify ... not yet changed to 0.11):\\r\\n\\r\\nFor [wiki:TracModWSGI mod_wsgi]:\\r\\n{{{\\r\\nos.environ[''TRAC_ENV_INDEX_TEMPLATE''] = ''/path/to/template.html''\\r\\n}}}\\r\\n\\r\\nFor [wiki:TracFastCgi FastCGI]:\\r\\n{{{\\r\\nFastCgiConfig -initial-env TRAC_ENV_PARENT_DIR=/parent/dir/of/projects \\\\\\r\\n              -initial-env TRAC_ENV_INDEX_TEMPLATE=/path/to/template\\r\\n}}}\\r\\n\\r\\nFor [wiki:TracModPython mod_python]:\\r\\n{{{\\r\\nPythonOption TracEnvParentDir /parent/dir/of/projects\\r\\nPythonOption TracEnvIndexTemplate /path/to/template\\r\\n}}}\\r\\n\\r\\nFor [wiki:TracCgi CGI]:\\r\\n{{{\\r\\nSetEnv TRAC_ENV_INDEX_TEMPLATE /path/to/template\\r\\n}}}\\r\\n\\r\\nFor [wiki:TracStandalone], you''ll need to set up the `TRAC_ENV_INDEX_TEMPLATE` environment variable in the shell used to launch tracd:\\r\\n - Unix\\r\\n   {{{\\r\\n#!sh\\r\\n$ export TRAC_ENV_INDEX_TEMPLATE=/path/to/template\\r\\n   }}}\\r\\n - Windows\\r\\n   {{{\\r\\n#!sh\\r\\n$ set TRAC_ENV_INDEX_TEMPLATE=/path/to/template\\r\\n   }}}\\r\\n\\r\\n== Project Templates ==\\r\\n\\r\\nThe appearance of each individual Trac environment (that is, instance of a project) can be customized independently of other projects, even those hosted by the same server. The recommended way is to use a `site.html` template (see [#SiteAppearance]) whenever possible. Using `site.html` means changes are made to the original templates as they are rendered, and you should not normally need to redo modifications whenever Trac is upgraded. If you do make a copy of `theme.html` or any other Trac template, you need to migrate your modifiations to the newer version - if not, new Trac features or bug fixes may not work as expected.\\r\\n\\r\\nWith that word of caution, any Trac template may be copied and customized. The default Trac templates are located inside the installed Trac egg (`/usr/lib/pythonVERSION/site-packages/Trac-VERSION.egg/trac/templates, .../trac/ticket/templates, .../trac/wiki/templates, ++`). The [#ProjectList] template file is called `index.html`, while the template responsible for main layout is called `theme.html`. Page assets such as images and CSS style sheets are located in the egg''s `trac/htdocs` directory.\\r\\n\\r\\nHowever, do not edit templates or site resources inside the Trac egg - installing Trac again can completely delete your modifications. Instead use one of two alternatives:\\r\\n * For a modification to one project only, copy the template to project `templates` directory.\\r\\n * For a modification shared by several projects, copy the template to a shared location and have each project point to this location using the `[inherit] templates_dir =` trac.ini option.\\r\\n\\r\\nTrac resolves requests for a template by first looking inside the project, then in any inherited templates location, and finally inside the Trac egg.\\r\\n\\r\\nTrac caches templates in memory by default to improve performance. To apply a template you need to restart the server.\\r\\n\\r\\n----\\r\\nSee also TracGuide, TracIni\\r\\n', NULL, NULL);\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('TracModWSGI', 1, 1362994208275927, 'trac', '127.0.0.1', '= Trac and mod_wsgi =\\r\\n\\r\\n\\r\\n[http://code.google.com/p/modwsgi/ mod_wsgi] is an Apache module for running WSGI-compatible Python applications directly on top of the Apache webserver. The mod_wsgi adapter is written completely in C and provides very good performances.\\r\\n\\r\\n[[PageOutline(2-3,Overview,inline)]]\\r\\n\\r\\n== The `trac.wsgi` script\\r\\n\\r\\nTrac can be run on top of mod_wsgi with the help of the following application script, which is just a Python file, though usually saved with a `.wsgi` extension). \\r\\n\\r\\n=== A very basic script\\r\\nIn its simplest form, the script could be:\\r\\n\\r\\n{{{#!python\\r\\nimport os\\r\\n\\r\\nos.environ[''TRAC_ENV''] = ''/usr/local/trac/mysite''\\r\\nos.environ[''PYTHON_EGG_CACHE''] = ''/usr/local/trac/mysite/eggs''\\r\\n\\r\\nimport trac.web.main\\r\\napplication = trac.web.main.dispatch_request\\r\\n}}}\\r\\n\\r\\nThe `TRAC_ENV` variable should naturally be the directory for your Trac environment (if you have several Trac environments in a directory, you can also use `TRAC_ENV_PARENT_DIR` instead), while the `PYTHON_EGG_CACHE` should be a directory where Python can temporarily extract Python eggs.\\r\\n\\r\\n=== A more elaborate script\\r\\n\\r\\nIf you''re using multiple `.wsgi` files (for example one per Trac environment) you must ''''not'''' use `os.environ[''TRAC_ENV'']` to set the path to the Trac environment. Using this method may lead to Trac delivering the content of another Trac environment, as the variable may be filled with the path of a previously viewed Trac environment. \\r\\n\\r\\nTo solve this problem, use the following `.wsgi` file instead:\\r\\n{{{#!python\\r\\nimport os\\r\\n\\r\\nos.environ[''PYTHON_EGG_CACHE''] = ''/usr/local/trac/mysite/eggs''\\r\\n\\r\\nimport trac.web.main\\r\\ndef application(environ, start_response):\\r\\n  environ[''trac.env_path''] = ''/usr/local/trac/mysite'' \\r\\n  return trac.web.main.dispatch_request(environ, start_response)\\r\\n}}}\\r\\n\\r\\nFor clarity, you should give this file a `.wsgi` extension. You should probably put the file in its own directory, since you will expose it to Apache. \\r\\n\\r\\nIf you have installed Trac and eggs in a path different from the standard one you should add that path by adding the following code at the top of the wsgi script:\\r\\n\\r\\n{{{#!python\\r\\nimport site\\r\\nsite.addsitedir(''/usr/local/trac/lib/python2.4/site-packages'')\\r\\n}}}\\r\\n\\r\\nChange it according to the path you installed the Trac libs at.\\r\\n\\r\\n=== Recommended `trac.wsgi` script\\r\\n\\r\\nA somewhat robust and generic version of this file can be created using the `trac-admin <env> deploy <dir>` command which automatically substitutes the required paths (see TracInstall#cgi-bin).\\r\\n\\r\\n\\r\\n== Mapping requests to the script\\r\\n\\r\\nAfter you''ve done preparing your .wsgi script, add the following to your Apache configuration file (`httpd.conf` for example).\\r\\n\\r\\n{{{\\r\\nWSGIScriptAlias /trac /usr/local/trac/mysite/apache/mysite.wsgi\\r\\n\\r\\n<Directory /usr/local/trac/mysite/apache>\\r\\n    WSGIApplicationGroup %{GLOBAL}\\r\\n    Order deny,allow\\r\\n    Allow from all\\r\\n</Directory>\\r\\n}}}\\r\\n\\r\\nHere, the script is in a subdirectory of the Trac environment.\\r\\n\\r\\nIf you followed the directions [http://trac.edgewall.org/wiki/TracInstall#cgi-bin Generating the Trac cgi-bin directory], your Apache configuration file should look like following:\\r\\n\\r\\n{{{\\r\\nWSGIScriptAlias /trac /usr/share/trac/cgi-bin/trac.wsgi\\r\\n\\r\\n<Directory /usr/share/trac/cgi-bin>\\r\\n    WSGIApplicationGroup %{GLOBAL}\\r\\n    Order deny,allow\\r\\n    Allow from all\\r\\n</Directory>\\r\\n}}}\\r\\n\\r\\nIn order to let Apache run the script, access to the directory in which the script resides is opened up to all of Apache. Additionally, the `WSGIApplicationGroup` directive ensures that Trac is always run in the first Python interpreter created by mod_wsgi; this is necessary because the Subversion Python bindings, which are used by Trac, don''t always work in other sub-interpreters and may cause requests to hang or cause Apache to crash as a result. After adding this configuration, restart Apache, and then it should work.\\r\\n\\r\\nTo test the setup of Apache, mod_wsgi and Python itself (ie. without involving Trac and dependencies), this simple wsgi application can be used to make sure that requests gets served (use as only content in your `.wsgi` script):\\r\\n\\r\\n{{{#!python\\r\\ndef application(environ, start_response):\\r\\n        start_response(''200 OK'',[(''Content-type'',''text/html'')])\\r\\n        return [''<html><body>Hello World!</body></html>'']\\r\\n}}}\\r\\n\\r\\nFor more information about using the mod_wsgi specific directives, see the [http://code.google.com/p/modwsgi/wiki/ mod_wsgi''s wiki] and more specifically the [http://code.google.com/p/modwsgi/wiki/IntegrationWithTrac IntegrationWithTrac] page.\\r\\n\\r\\n\\r\\n== Configuring Authentication\\r\\n\\r\\nWe describe in the the following sections different methods for setting up authentication.\\r\\n\\r\\nSee also [http://httpd.apache.org/docs/2.2/howto/auth.html Authentication, Authorization and Access Control] in the Apache guide.\\r\\n\\r\\n=== Using Basic Authentication ===\\r\\n\\r\\nThe simplest way to enable authentication with Apache is to create a password file. Use the `htpasswd` program to create the password file:\\r\\n{{{\\r\\n$ htpasswd -c /somewhere/trac.htpasswd admin\\r\\nNew password: <type password>\\r\\nRe-type new password: <type password again>\\r\\nAdding password for user admin\\r\\n}}}\\r\\n\\r\\nAfter the first user, you dont need the \"-c\" option anymore:\\r\\n{{{\\r\\n$ htpasswd /somewhere/trac.htpasswd john\\r\\nNew password: <type password>\\r\\nRe-type new password: <type password again>\\r\\nAdding password for user john\\r\\n}}}\\r\\n\\r\\n  ''''See the man page for `htpasswd` for full documentation.''''\\r\\n\\r\\nAfter you''ve created the users, you can set their permissions using TracPermissions.\\r\\n\\r\\nNow, you''ll need to enable authentication against the password file in the Apache configuration:\\r\\n{{{\\r\\n<Location \"/trac/login\">\\r\\n  AuthType Basic\\r\\n  AuthName \"Trac\"\\r\\n  AuthUserFile /somewhere/trac.htpasswd\\r\\n  Require valid-user\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nIf you''re hosting multiple projects you can use the same password file for all of them:\\r\\n{{{\\r\\n<LocationMatch \"/trac/[^/]+/login\">\\r\\n  AuthType Basic\\r\\n  AuthName \"Trac\"\\r\\n  AuthUserFile /somewhere/trac.htpasswd\\r\\n  Require valid-user\\r\\n</LocationMatch>\\r\\n}}}\\r\\nNote that neither a file nor a directory named ''login'' needs to exist.[[BR]]\\r\\nSee also the [http://httpd.apache.org/docs/2.2/mod/mod_auth_basic.html mod_auth_basic] documentation.\\r\\n\\r\\n=== Using Digest Authentication ===\\r\\n\\r\\nFor better security, it is recommended that you either enable SSL or at least use the “digest” authentication scheme instead of “Basic”. \\r\\n\\r\\nYou''ll have to create your `.htpasswd` file with the `htdigest` command instead of `htpasswd`, as follows:\\r\\n{{{\\r\\n# htdigest -c /somewhere/trac.htpasswd trac admin\\r\\n}}}\\r\\n\\r\\nThe \"trac\" parameter above is the \"realm\", and will have to be reused in the Apache configuration in the !AuthName directive:\\r\\n\\r\\n{{{\\r\\n<Location \"/trac/login\">\\r\\n\\r\\n    AuthType Digest\\r\\n    AuthName \"trac\"\\r\\n    AuthDigestDomain /trac\\r\\n    AuthUserFile /somewhere/trac.htpasswd\\r\\n    Require valid-user\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nFor multiple environments, you can use the same `LocationMatch` as described with the previous method.\\r\\n\\r\\nDon''t forget to activate the mod_auth_digest. For example, on a Debian 4.0r1 (etch) system:\\r\\n{{{\\r\\n    LoadModule auth_digest_module /usr/lib/apache2/modules/mod_auth_digest.so\\r\\n}}}\\r\\n\\r\\n\\r\\nSee also the [http://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html mod_auth_digest] documentation.\\r\\n\\r\\n=== Using LDAP Authentication \\r\\n\\r\\nConfiguration for [http://httpd.apache.org/docs/2.2/mod/mod_ldap.html mod_ldap] authentication in Apache is a bit tricky (httpd 2.2.x and OpenLDAP: slapd 2.3.19)\\r\\n\\r\\n1. You need to load the following modules in Apache httpd.conf\\r\\n{{{\\r\\nLoadModule ldap_module modules/mod_ldap.so\\r\\nLoadModule authnz_ldap_module modules/mod_authnz_ldap.so\\r\\n}}}\\r\\n\\r\\n2. Your httpd.conf also needs to look something like:\\r\\n\\r\\n{{{\\r\\n<Location /trac/>\\r\\n  # (if you''re using it, mod_python specific settings go here)\\r\\n  Order deny,allow\\r\\n  Deny from all\\r\\n  Allow from 192.168.11.0/24\\r\\n  AuthType Basic\\r\\n  AuthName \"Trac\"\\r\\n  AuthBasicProvider \"ldap\"\\r\\n  AuthLDAPURL \"ldap://127.0.0.1/dc=example,dc=co,dc=ke?uid?sub?(objectClass=inetOrgPerson)\"\\r\\n  authzldapauthoritative Off\\r\\n  Require valid-user\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\n\\r\\n3. You can use the LDAP interface as a way to authenticate to a Microsoft Active Directory:\\r\\n\\r\\n\\r\\nUse the following as your LDAP URL:\\r\\n{{{\\r\\n    AuthLDAPURL \"ldap://directory.example.com:3268/DC=example,DC=com?sAMAccountName?sub?(objectClass=user)\"\\r\\n}}}\\r\\n\\r\\nYou will also need to provide an account for Apache to use when checking\\r\\ncredentials. As this password will be listed in plaintext in the\\r\\nconfig, you should be sure to use an account specifically for this task:\\r\\n{{{\\r\\n    AuthLDAPBindDN ldap-auth-user@example.com\\r\\n    AuthLDAPBindPassword \"password\"\\r\\n}}}\\r\\n\\r\\nThe whole section looks like:\\r\\n{{{\\r\\n<Location /trac/>\\r\\n  # (if you''re using it, mod_python specific settings go here)\\r\\n  Order deny,allow\\r\\n  Deny from all\\r\\n  Allow from 192.168.11.0/24\\r\\n  AuthType Basic\\r\\n  AuthName \"Trac\"\\r\\n  AuthBasicProvider \"ldap\"\\r\\n  AuthLDAPURL \"ldap://adserver.company.com:3268/DC=company,DC=com?sAMAccountName?sub?(objectClass=user)\"\\r\\n  AuthLDAPBindDN       ldap-auth-user@company.com\\r\\n  AuthLDAPBindPassword \"the_password\"\\r\\n  authzldapauthoritative Off\\r\\n  # require valid-user\\r\\n  require ldap-group CN=Trac Users,CN=Users,DC=company,DC=com\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nNote 1: This is the case where the LDAP search will get around the multiple OUs, conecting to Global Catalog Server portion of AD (Notice the port is 3268, not the normal LDAP 389). The GCS is basically a \"flattened\" tree which allows searching for a user without knowing to which OU they belong.\\r\\n\\r\\nNote 2: You can also require the user be a member of a certain LDAP group, instead of\\r\\njust having a valid login:\\r\\n{{{\\r\\n    Require ldap-group CN=Trac Users,CN=Users,DC=example,DC=com\\r\\n}}}\\r\\n\\r\\nSee also:\\r\\n  - [http://httpd.apache.org/docs/2.2/mod/mod_authnz_ldap.html mod_authnz_ldap], documentation for mod_authnz_ldap\\r\\n    \\r\\n - [http://httpd.apache.org/docs/2.2/mod/mod_ldap.html mod_ldap], documentation for mod_ldap, which provides connection pooling and a shared cache.\\r\\n - [http://trac-hacks.org/wiki/LdapPlugin TracHacks:LdapPlugin] for storing TracPermissions in LDAP.\\r\\n\\r\\n=== Using SSPI Authentication\\r\\n\\r\\nIf you are using Apache on Windows, you can use mod_auth_sspi to provide\\r\\nsingle-sign-on. Download the module from the !SourceForge [http://sourceforge.net/projects/mod-auth-sspi/ mod-auth-sspi project] and then add the\\r\\nfollowing to your !VirtualHost:\\r\\n{{{\\r\\n    <Location /trac/login>\\r\\n        AuthType SSPI\\r\\n        AuthName \"Trac Login\"\\r\\n        SSPIAuth On\\r\\n        SSPIAuthoritative On\\r\\n        SSPIDomain MyLocalDomain\\r\\n        SSPIOfferBasic On\\r\\n        SSPIOmitDomain Off\\r\\n        SSPIBasicPreferred On\\r\\n        Require valid-user\\r\\n    </Location>\\r\\n}}}\\r\\n\\r\\nUsing the above, usernames in Trac will be of the form `DOMAIN\\\\username`, so\\r\\nyou may have to re-add permissions and such. If you do not want the domain to\\r\\nbe part of the username, set `SSPIOmitDomain On` instead.\\r\\n\\r\\nSome common problems with SSPI authentication: [trac:#1055], [trac:#1168] and [trac:#3338].\\r\\n\\r\\nSee also [trac:TracOnWindows/Advanced].\\r\\n\\r\\n=== Using Apache authentication with the Account Manager plugin''s Login form ===\\r\\n\\r\\nTo begin with, see the basic instructions for using the Account Manager plugin''s [http://trac-hacks.org/wiki/AccountManagerPlugin/Modules#LoginModule Login module] and its [http://trac-hacks.org/wiki/AccountManagerPlugin/AuthStores#HttpAuthStore HttpAuthStore authentication module].\\r\\n\\r\\n''''''Note:'''''' If is difficult to get !HttpAuthStore to work with WSGI when using any Account Manager version prior to acct_mgr-0.4. Upgrading is recommended.\\r\\n\\r\\nHere is an example (from the !HttpAuthStore link) using acct_mgr-0.4 for hosting a single project:\\r\\n{{{\\r\\n[components]\\r\\n; be sure to enable the component\\r\\nacct_mgr.http.HttpAuthStore = enabled\\r\\n\\r\\n[account-manager]\\r\\n; configure the plugin to use a page that is secured with http authentication\\r\\nauthentication_url = /authFile\\r\\npassword_store = HttpAuthStore\\r\\n}}}\\r\\nThis will generally be matched with an Apache config like:\\r\\n{{{\\r\\n<Location /authFile>\\r\\n   …HTTP authentication configuration…\\r\\n   Require valid-user\\r\\n</Location>\\r\\n}}}\\r\\nNote that ''''''authFile'''''' need not exist. See the !HttpAuthStore link above for examples where multiple Trac projects are hosted on a server.\\r\\n\\r\\n=== Example: Apache/mod_wsgi with Basic Authentication, Trac being at the root of a virtual host\\r\\n\\r\\nPer the mod_wsgi documentation linked to above, here is an example Apache configuration that a) serves the Trac instance from a virtualhost subdomain and b) uses Apache basic authentication for Trac authentication.\\r\\n\\r\\n\\r\\nIf you want your Trac to be served from e.g. !http://trac.my-proj.my-site.org, then from the folder e.g. `/home/trac-for-my-proj`, if you used the command `trac-admin the-env initenv` to create a folder `the-env`, and you used `trac-admin the-env deploy the-deploy` to create a folder `the-deploy`, then first:\\r\\n\\r\\nCreate the htpasswd file:\\r\\n{{{\\r\\ncd /home/trac-for-my-proj/the-env\\r\\nhtpasswd -c htpasswd firstuser\\r\\n### and add more users to it as needed:\\r\\nhtpasswd htpasswd seconduser\\r\\n}}}\\r\\n(keep the file above your document root for security reasons)\\r\\n\\r\\nCreate this file e.g. (ubuntu) `/etc/apache2/sites-enabled/trac.my-proj.my-site.org.conf` with the following contents:\\r\\n\\r\\n{{{\\r\\n<Directory /home/trac-for-my-proj/the-deploy/cgi-bin/trac.wsgi>\\r\\n  WSGIApplicationGroup %{GLOBAL}\\r\\n  Order deny,allow\\r\\n  Allow from all\\r\\n</Directory>\\r\\n\\r\\n<VirtualHost *:80>\\r\\n  ServerName trac.my-proj.my-site.org\\r\\n  DocumentRoot /home/trac-for-my-proj/the-env/htdocs/\\r\\n  WSGIScriptAlias / /home/trac-for-my-proj/the-deploy/cgi-bin/trac.wsgi\\r\\n  <Location ''/''>\\r\\n    AuthType Basic\\r\\n    AuthName \"Trac\"\\r\\n    AuthUserFile /home/trac-for-my-proj/the-env/htpasswd\\r\\n    Require valid-user\\r\\n  </Location>\\r\\n</VirtualHost>\\r\\n\\r\\n}}}\\r\\n\\r\\nNote: for subdomains to work you would probably also need to alter `/etc/hosts` and add A-Records to your host''s DNS.\\r\\n\\r\\n\\r\\n== Troubleshooting\\r\\n\\r\\n=== Use a recent version\\r\\n\\r\\nPlease use either version 1.6, 2.4 or later of `mod_wsgi`. Versions prior to 2.4 in the 2.X branch have problems with some Apache configurations that use WSGI file wrapper extension. This extension is used in Trac to serve up attachments and static media files such as style sheets. If you are affected by this problem attachments will appear to be empty and formatting of HTML pages will appear not to work due to style sheet files not loading properly. Another frequent symptom is that binary attachment downloads are truncated. See mod_wsgi tickets [http://code.google.com/p/modwsgi/issues/detail?id=100 #100] and [http://code.google.com/p/modwsgi/issues/detail?id=132 #132].\\r\\n\\r\\n''''Note: using mod_wsgi 2.5 and Python 2.6.1 gave an Internal Server Error on my system (Apache 2.2.11 and Trac 0.11.2.1). Upgrading to Python 2.6.2 (as suggested [http://www.mail-archive.com/modwsgi@googlegroups.com/msg01917.html here]) solved this for me[[BR]]-- Graham Shanks''''\\r\\n\\r\\nIf you plan to use `mod_wsgi` in embedded mode on Windows or with the MPM worker on Linux, then you''ll even need version 0.3.4 or greater (see [trac:#10675] for details).\\r\\n\\r\\n=== Getting Trac to work nicely with SSPI and ''Require Group'' ===\\r\\nIf like me you''ve set Trac up on Apache, Win32 and configured SSPI, but added a ''Require group'' option to your apache configuration, then the SSPIOmitDomain option is probably not working.  If its not working your usernames in trac are probably looking like ''DOMAIN\\\\user'' rather than ''user''.\\r\\n\\r\\nThis WSGI script ''fixes'' things, hope it helps:\\r\\n{{{#!python\\r\\nimport os\\r\\nimport trac.web.main\\r\\n\\r\\nos.environ[''TRAC_ENV''] = ''/usr/local/trac/mysite''\\r\\nos.environ[''PYTHON_EGG_CACHE''] = ''/usr/local/trac/mysite/eggs''\\r\\n\\r\\ndef application(environ, start_response):\\r\\n    if \"\\\\\\\\\" in environ[''REMOTE_USER'']:\\r\\n        environ[''REMOTE_USER''] = environ[''REMOTE_USER''].split(\"\\\\\\\\\", 1)[1]\\r\\n    return trac.web.main.dispatch_request(environ, start_response)\\r\\n}}}\\r\\n\\r\\n\\r\\n=== Trac with PostgreSQL ===\\r\\n\\r\\nWhen using the mod_wsgi adapter with multiple Trac instances and PostgreSQL (or MySQL?) as a database back-end, the server ''''may'''' create a lot of open database connections and thus PostgreSQL processes.\\r\\n\\r\\nA somewhat brutal workaround is to disabled connection pooling in Trac. This is done by setting `poolable = False` in `trac.db.postgres_backend` on the `PostgreSQLConnection` class.\\r\\n\\r\\nBut it''s not necessary to edit the source of Trac, the following lines in `trac.wsgi` will also work:\\r\\n\\r\\n{{{\\r\\nimport trac.db.postgres_backend\\r\\ntrac.db.postgres_backend.PostgreSQLConnection.poolable = False\\r\\n}}}\\r\\n\\r\\nor\\r\\n\\r\\n{{{\\r\\nimport trac.db.mysql_backend\\r\\ntrac.db.mysql_backend.MySQLConnection.poolable = False\\r\\n}}}\\r\\n\\r\\nNow Trac drops the connection after serving a page and the connection count on the database will be kept minimal.\\r\\n\\r\\n//This is not a recommended approach though. See also the notes at the bottom of the [http://code.google.com/p/modwsgi/wiki/IntegrationWithTrac mod_wsgi''s IntegrationWithTrac] wiki page.//\\r\\n\\r\\n=== Other resources\\r\\n\\r\\nFor more troubleshooting tips, see also the [TracModPython#Troubleshooting mod_python troubleshooting] section, as most Apache-related issues are quite similar, plus discussion of potential [http://code.google.com/p/modwsgi/wiki/ApplicationIssues application issues] when using mod_wsgi. The wsgi page also has a [http://code.google.com/p/modwsgi/wiki/IntegrationWithTrac Integration With Trac] document.\\r\\n\\r\\n\\r\\n----\\r\\nSee also:  TracGuide, TracInstall, [wiki:TracFastCgi FastCGI], [wiki:TracModPython ModPython], [trac:TracNginxRecipe TracNginxRecipe]\\r\\n', NULL, NULL),\n('WikiDeletePage', 1, 1362994208280549, 'trac', '127.0.0.1', '= Deleting a Wiki Page =\\r\\n\\r\\nExisting wiki pages can be completely deleted using the ''''Delete Page'''' or the ''''Delete this Version'''' buttons at the bottom of the wiki page. These buttons are only visible for users with `WIKI_DELETE` permissions.\\r\\n\\r\\n''''''Note:'''''' Deleting a wiki page is an irreversible operation.\\r\\n\\r\\nIf you want to delete a page because you actually re-created a new page with the same content but a different name, it is recommended to keep the page and use it as a redirection page instead of completely deleting it, as to not frustrate the visitor with broken links when coming to the site from a search engine. \\r\\n\\r\\nIn this situation, chances are that you actually wanted to [[WikiNewPage#renaming|rename]] the page instead of doing a copy + delete. \\r\\nThe ''''Rename'''' operation also offers you the possibility to create a redirection page.\\r\\nA redirection page is a short page that  contains a link such as  “See !SomeOtherPage”. \\r\\n\\r\\nHowever, deleting specific versions or even complete pages can make sense to remove spam or other abusive submissions.\\r\\n\\r\\n----\\r\\nSee also: TracWiki, TracPermissions\\r\\n', NULL, NULL),\n('TracStandalone', 1, 1362994208283052, 'trac', '127.0.0.1', '= Tracd =\\r\\n\\r\\nTracd is a lightweight standalone Trac web server.\\r\\nIt can be used in a variety of situations, from a test or development server to a multiprocess setup behind another web server used as a load balancer.\\r\\n\\r\\n== Pros ==\\r\\n\\r\\n * Fewer dependencies: You don''t need to install apache or any other web-server.\\r\\n * Fast: Should be almost as fast as the [wiki:TracModPython mod_python] version (and much faster than the [wiki:TracCgi CGI]), even more so since version 0.12 where the HTTP/1.1 version of the protocol is enabled by default\\r\\n * Automatic reloading: For development, Tracd can be used in ''''auto_reload'''' mode, which will automatically restart the server whenever you make a change to the code (in Trac itself or in a plugin).\\r\\n\\r\\n== Cons ==\\r\\n\\r\\n * Fewer features: Tracd implements a very simple web-server and is not as configurable or as scalable as Apache httpd.\\r\\n * No native HTTPS support: [http://www.rickk.com/sslwrap/ sslwrap] can be used instead,\\r\\n   or [http://trac.edgewall.org/wiki/STunnelTracd stunnel -- a tutorial on how to use stunnel with tracd] or Apache with mod_proxy.\\r\\n\\r\\n== Usage examples ==\\r\\n\\r\\nA single project on port 8080. (http://localhost:8080/)\\r\\n{{{\\r\\n $ tracd -p 8080 /path/to/project\\r\\n}}}\\r\\nStricly speaking this will make your Trac accessible to everybody from your network rather than ''''localhost only''''. To truly limit it use ''''--hostname'''' option.\\r\\n{{{\\r\\n $ tracd --hostname=localhost -p 8080 /path/to/project\\r\\n}}}\\r\\nWith more than one project. (http://localhost:8080/project1/ and http://localhost:8080/project2/)\\r\\n{{{\\r\\n $ tracd -p 8080 /path/to/project1 /path/to/project2\\r\\n}}}\\r\\n\\r\\nYou can''t have the last portion of the path identical between the projects since Trac uses that name to keep the URLs of the\\r\\ndifferent projects unique. So if you use `/project1/path/to` and `/project2/path/to`, you will only see the second project.\\r\\n\\r\\nAn alternative way to serve multiple projects is to specify a parent directory in which each subdirectory is a Trac project, using the `-e` option. The example above could be rewritten:\\r\\n{{{\\r\\n $ tracd -p 8080 -e /path/to\\r\\n}}}\\r\\n\\r\\nTo exit the server on Windows, be sure to use {{{CTRL-BREAK}}} -- using {{{CTRL-C}}} will leave a Python process running in the background.\\r\\n\\r\\n== Installing as a Windows Service ==\\r\\n\\r\\n=== Option 1 ===\\r\\nTo install as a Windows service, get the [http://www.google.com/search?q=srvany.exe SRVANY] utility and run:\\r\\n{{{\\r\\n C:\\\\path\\\\to\\\\instsrv.exe tracd C:\\\\path\\\\to\\\\srvany.exe\\r\\n reg add HKLM\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\tracd\\\\Parameters /v Application /d \"\\\\\"C:\\\\path\\\\to\\\\python.exe\\\\\" \\\\\"C:\\\\path\\\\to\\\\python\\\\scripts\\\\tracd-script.py\\\\\" <your tracd parameters>\"\\r\\n net start tracd\\r\\n}}}\\r\\n\\r\\n''''''DO NOT'''''' use {{{tracd.exe}}}.  Instead register {{{python.exe}}} directly with {{{tracd-script.py}}} as a parameter.  If you use {{{tracd.exe}}}, it will spawn the python process without SRVANY''s knowledge.  This python process will survive a {{{net stop tracd}}}.\\r\\n\\r\\nIf you want tracd to start automatically when you boot Windows, do:\\r\\n{{{\\r\\n sc config tracd start= auto\\r\\n}}}\\r\\n\\r\\nThe spacing here is important.\\r\\n\\r\\n{{{#!div\\r\\nOnce the service is installed, it might be simpler to run the Registry Editor rather than use the `reg add` command documented above.  Navigate to:[[BR]]\\r\\n`HKEY_LOCAL_MACHINE\\\\SYSTEM\\\\CurrentControlSet\\\\Services\\\\tracd\\\\Parameters`\\r\\n\\r\\nThree (string) parameters are provided:\\r\\n||!AppDirectory ||C:\\\\Python26\\\\ ||\\r\\n||Application ||python.exe ||\\r\\n||!AppParameters ||scripts\\\\tracd-script.py -p 8080 ... ||\\r\\n\\r\\nNote that, if the !AppDirectory is set as above, the paths of the executable ''''and'''' of the script name and parameter values are relative to the directory.  This makes updating Python a little simpler because the change can be limited, here, to a single point.\\r\\n(This is true for the path to the .htpasswd file, as well, despite the documentation calling out the /full/path/to/htpasswd; however, you may not wish to store that file under the Python directory.)\\r\\n}}}\\r\\n\\r\\nFor Windows 7 User, srvany.exe may not be an option, so you can use [http://www.google.com/search?q=winserv.exe WINSERV] utility and run:\\r\\n{{{\\r\\n\"C:\\\\path\\\\to\\\\winserv.exe\" install tracd -displayname \"tracd\" -start auto \"C:\\\\path\\\\to\\\\python.exe\" c:\\\\path\\\\to\\\\python\\\\scripts\\\\tracd-script.py <your tracd parameters>\"\\r\\n\\r\\nnet start tracd\\r\\n}}}\\r\\n\\r\\n=== Option 2 ===\\r\\n\\r\\nUse [http://trac-hacks.org/wiki/WindowsServiceScript WindowsServiceScript], available at [http://trac-hacks.org/ Trac Hacks]. Installs, removes, starts, stops, etc. your Trac service.\\r\\n\\r\\n=== Option 3 ===\\r\\n\\r\\nalso cygwin''s cygrunsrv.exe can be used:\\r\\n{{{\\r\\n$ cygrunsrv --install tracd --path /cygdrive/c/Python27/Scripts/tracd.exe --args ''--port 8000 --env-parent-dir E:\\\\IssueTrackers\\\\Trac\\\\Projects''\\r\\n$ net start tracd\\r\\n}}}\\r\\n\\r\\n== Using Authentication ==\\r\\n\\r\\nTracd allows you to run Trac without the need for Apache, but you can take advantage of Apache''s password tools (htpasswd and htdigest) to easily create a password file in the proper format for tracd to use in authentication. (It is also possible to create the password file without htpasswd or htdigest; see below for alternatives)\\r\\n\\r\\nTracd provides support for both Basic and Digest authentication. Digest is considered more secure. The examples below use Digest; to use Basic authentication, replace `--auth` with `--basic-auth` in the command line.\\r\\n\\r\\nThe general format for using authentication is:\\r\\n{{{\\r\\n $ tracd -p port --auth=\"base_project_dir,password_file_path,realm\" project_path\\r\\n}}}\\r\\nwhere:\\r\\n * ''''''base_project_dir'''''': the base directory of the project specified as follows:\\r\\n   * when serving multiple projects: ''''relative'''' to the `project_path`\\r\\n   * when serving only a single project (`-s`): the name of the project directory\\r\\n Don''t use an absolute path here as this won''t work. ''''Note:'''' This parameter is case-sensitive even for environments on Windows.\\r\\n * ''''''password_file_path'''''': path to the password file\\r\\n * ''''''realm'''''': the realm name (can be anything)\\r\\n * ''''''project_path'''''': path of the project\\r\\n\\r\\n * **`--auth`** in the above means use Digest authentication, replace `--auth` with `--basic-auth` if you want to use Basic auth.  Although Basic authentication does not require a \"realm\", the command parser does, so the second comma is required, followed directly by the closing quote for an empty realm name.\\r\\n\\r\\nExamples:\\r\\n\\r\\n{{{\\r\\n $ tracd -p 8080 \\\\\\r\\n   --auth=\"project1,/path/to/passwordfile,mycompany.com\" /path/to/project1\\r\\n}}}\\r\\n\\r\\nOf course, the password file can be be shared so that it is used for more than one project:\\r\\n{{{\\r\\n $ tracd -p 8080 \\\\\\r\\n   --auth=\"project1,/path/to/passwordfile,mycompany.com\" \\\\\\r\\n   --auth=\"project2,/path/to/passwordfile,mycompany.com\" \\\\\\r\\n   /path/to/project1 /path/to/project2\\r\\n}}}\\r\\n\\r\\nAnother way to share the password file is to specify \"*\" for the project name:\\r\\n{{{\\r\\n $ tracd -p 8080 \\\\\\r\\n   --auth=\"*,/path/to/users.htdigest,mycompany.com\" \\\\\\r\\n   /path/to/project1 /path/to/project2\\r\\n}}}\\r\\n\\r\\n=== Basic Authorization: Using a htpasswd password file ===\\r\\nThis section describes how to use `tracd` with Apache .htpasswd files.\\r\\n\\r\\n  Note: It is necessary (at least with Python 2.6) to install the fcrypt package in order to\\r\\n  decode some htpasswd formats.  Trac source code attempt an `import crypt` first, but there\\r\\n  is no such package for Python 2.6. Only `SHA-1` passwords (since Trac 1.0) work without this module.\\r\\n\\r\\nTo create a .htpasswd file use Apache''s `htpasswd` command (see [#GeneratingPasswordsWithoutApache below] for a method to create these files without using Apache):\\r\\n{{{\\r\\n $ sudo htpasswd -c /path/to/env/.htpasswd username\\r\\n}}}\\r\\nthen for additional users:\\r\\n{{{\\r\\n $ sudo htpasswd /path/to/env/.htpasswd username2\\r\\n}}}\\r\\n\\r\\nThen to start `tracd` run something like this:\\r\\n{{{\\r\\n $ tracd -p 8080 --basic-auth=\"projectdirname,/fullpath/environmentname/.htpasswd,realmname\" /fullpath/environmentname\\r\\n}}}\\r\\n\\r\\nFor example:\\r\\n{{{\\r\\n $ tracd -p 8080 --basic-auth=\"testenv,/srv/tracenv/testenv/.htpasswd,My Test Env\" /srv/tracenv/testenv\\r\\n}}}\\r\\n''''Note:'''' You might need to pass \"-m\" as a parameter to htpasswd on some platforms (OpenBSD).\\r\\n\\r\\n=== Digest authentication: Using a htdigest password file ===\\r\\n\\r\\nIf you have Apache available, you can use the htdigest command to generate the password file. Type ''htdigest'' to get some usage instructions, or read [http://httpd.apache.org/docs/2.0/programs/htdigest.html this page] from the Apache manual to get precise instructions.  You''ll be prompted for a password to enter for each user that you create.  For the name of the password file, you can use whatever you like, but if you use something like `users.htdigest` it will remind you what the file contains. As a suggestion, put it in your <projectname>/conf folder along with the [TracIni trac.ini] file.\\r\\n\\r\\nNote that you can start tracd without the `--auth` argument, but if you click on the ''''Login'''' link you will get an error.\\r\\n\\r\\n=== Generating Passwords Without Apache ===\\r\\n\\r\\nBasic Authorization can be accomplished via this [http://aspirine.org/htpasswd_en.html online HTTP Password generator] which also supports `SHA-1`.  Copy the generated password-hash line to the .htpasswd file on your system. Note that Windows Python lacks the \"crypt\" module that is the default hash type for htpasswd ; Windows Python can grok MD5 password hashes just fine and you should use MD5.\\r\\n\\r\\nYou can use this simple Python script to generate a ''''''digest'''''' password file:\\r\\n\\r\\n{{{\\r\\n#!python\\r\\nfrom optparse import OptionParser\\r\\n# The md5 module is deprecated in Python 2.5\\r\\ntry:\\r\\n    from hashlib import md5\\r\\nexcept ImportError:\\r\\n    from md5 import md5\\r\\nrealm = ''trac''\\r\\n\\r\\n# build the options\\r\\nusage = \"usage: %prog [options]\"\\r\\nparser = OptionParser(usage=usage)\\r\\nparser.add_option(\"-u\", \"--username\",action=\"store\", dest=\"username\", type = \"string\",\\r\\n                  help=\"the username for whom to generate a password\")\\r\\nparser.add_option(\"-p\", \"--password\",action=\"store\", dest=\"password\", type = \"string\",\\r\\n                  help=\"the password to use\")\\r\\nparser.add_option(\"-r\", \"--realm\",action=\"store\", dest=\"realm\", type = \"string\",\\r\\n                  help=\"the realm in which to create the digest\")\\r\\n(options, args) = parser.parse_args()\\r\\n\\r\\n# check options\\r\\nif (options.username is None) or (options.password is None):\\r\\n   parser.error(\"You must supply both the username and password\")\\r\\nif (options.realm is not None):\\r\\n   realm = options.realm\\r\\n   \\r\\n# Generate the string to enter into the htdigest file\\r\\nkd = lambda x: md5('':''.join(x)).hexdigest()\\r\\nprint '':''.join((options.username, realm, kd([options.username, realm, options.password])))\\r\\n}}}\\r\\n\\r\\nNote: If you use the above script you must set the realm in the `--auth` argument to ''''''`trac`''''''. Example usage (assuming you saved the script as trac-digest.py):\\r\\n\\r\\n{{{\\r\\n $ python trac-digest.py -u username -p password >> c:\\\\digest.txt\\r\\n $ tracd --port 8000 --auth=proj_name,c:\\\\digest.txt,trac c:\\\\path\\\\to\\\\proj_name\\r\\n}}}\\r\\n\\r\\n==== Using `md5sum`\\r\\nIt is possible to use `md5sum` utility to generate digest-password file:\\r\\n{{{\\r\\nuser=\\r\\nrealm=\\r\\npassword=\\r\\npath_to_file=\\r\\necho ${user}:${realm}:$(printf \"${user}:${realm}:${password}\" | md5sum - | sed -e ''s/\\\\s\\\\+-//'') > ${path_to_file}\\r\\n}}}\\r\\n\\r\\n== Reference ==\\r\\n\\r\\nHere''s the online help, as a reminder (`tracd --help`):\\r\\n{{{\\r\\nUsage: tracd [options] [projenv] ...\\r\\n\\r\\nOptions:\\r\\n  --version             show program''s version number and exit\\r\\n  -h, --help            show this help message and exit\\r\\n  -a DIGESTAUTH, --auth=DIGESTAUTH\\r\\n                        [projectdir],[htdigest_file],[realm]\\r\\n  --basic-auth=BASICAUTH\\r\\n                        [projectdir],[htpasswd_file],[realm]\\r\\n  -p PORT, --port=PORT  the port number to bind to\\r\\n  -b HOSTNAME, --hostname=HOSTNAME\\r\\n                        the host name or IP address to bind to\\r\\n  --protocol=PROTOCOL   http|scgi|ajp|fcgi\\r\\n  -q, --unquote         unquote PATH_INFO (may be needed when using ajp)\\r\\n  --http10              use HTTP/1.0 protocol version instead of HTTP/1.1\\r\\n  --http11              use HTTP/1.1 protocol version (default)\\r\\n  -e PARENTDIR, --env-parent-dir=PARENTDIR\\r\\n                        parent directory of the project environments\\r\\n  --base-path=BASE_PATH\\r\\n                        the initial portion of the request URL''s \"path\"\\r\\n  -r, --auto-reload     restart automatically when sources are modified\\r\\n  -s, --single-env      only serve a single project without the project list\\r\\n  -d, --daemonize       run in the background as a daemon\\r\\n  --pidfile=PIDFILE     when daemonizing, file to which to write pid\\r\\n  --umask=MASK          when daemonizing, file mode creation mask to use, in\\r\\n                        octal notation (default 022)\\r\\n  --group=GROUP         the group to run as\\r\\n  --user=USER           the user to run as\\r\\n}}}\\r\\n\\r\\nUse the -d option so that tracd doesn''t hang if you close the terminal window where tracd was started.\\r\\n\\r\\n== Tips ==\\r\\n\\r\\n=== Serving static content ===\\r\\n\\r\\nIf `tracd` is the only web server used for the project, \\r\\nit can also be used to distribute static content \\r\\n(tarballs, Doxygen documentation, etc.)\\r\\n\\r\\nThis static content should be put in the `$TRAC_ENV/htdocs` folder,\\r\\nand is accessed by URLs like `<project_URL>/chrome/site/...`.\\r\\n\\r\\nExample: given a `$TRAC_ENV/htdocs/software-0.1.tar.gz` file,\\r\\nthe corresponding relative URL would be `/<project_name>/chrome/site/software-0.1.tar.gz`, \\r\\nwhich in turn can be written as `htdocs:software-0.1.tar.gz` (TracLinks syntax) or `[/<project_name>/chrome/site/software-0.1.tar.gz]` (relative link syntax). \\r\\n\\r\\n ''''Support for `htdocs:` TracLinks syntax was added in version 0.10''''\\r\\n\\r\\n=== Using tracd behind a proxy\\r\\n\\r\\nIn some situations when you choose to use tracd behind Apache or another web server.\\r\\n\\r\\nIn this situation, you might experience issues with redirects, like being redirected to URLs with the wrong host or protocol. In this case (and only in this case), setting the `[trac] use_base_url_for_redirect` to `true` can help, as this will force Trac to use the value of `[trac] base_url` for doing the redirects.\\r\\n\\r\\nIf you''re using the AJP protocol to connect with `tracd` (which is possible if you have flup installed), then you might experience problems with double quoting. Consider adding the `--unquote` parameter.\\r\\n\\r\\nSee also [trac:TracOnWindowsIisAjp], [trac:TracNginxRecipe].\\r\\n\\r\\n=== Authentication for tracd behind a proxy\\r\\nIt is convenient to provide central external authentication to your tracd instances, instead of using {{{--basic-auth}}}. There is some discussion about this in #9206.\\r\\n\\r\\nBelow is example configuration based on Apache 2.2, mod_proxy, mod_authnz_ldap.\\r\\n\\r\\nFirst we bring tracd into Apache''s location namespace.\\r\\n\\r\\n{{{\\r\\n<Location /project/proxified>\\r\\n        Require ldap-group cn=somegroup, ou=Groups,dc=domain.com\\r\\n        Require ldap-user somespecificusertoo\\r\\n        ProxyPass http://localhost:8101/project/proxified/\\r\\n        # Turns out we don''t really need complicated RewriteRules here at all\\r\\n        RequestHeader set REMOTE_USER %{REMOTE_USER}s\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nThen we need a single file plugin to recognize HTTP_REMOTE_USER header as valid authentication source. HTTP headers like ''''''HTTP_FOO_BAR'''''' will get converted to ''''''Foo-Bar'''''' during processing. Name it something like ''''''remote-user-auth.py'''''' and drop it into ''''''proxified/plugins'''''' directory:\\r\\n{{{\\r\\n#!python\\r\\nfrom trac.core import *\\r\\nfrom trac.config import BoolOption\\r\\nfrom trac.web.api import IAuthenticator\\r\\n\\r\\nclass MyRemoteUserAuthenticator(Component):\\r\\n\\r\\n    implements(IAuthenticator)\\r\\n\\r\\n    obey_remote_user_header = BoolOption(''trac'', ''obey_remote_user_header'', ''false'', \\r\\n               \"\"\"Whether the ''Remote-User:'' HTTP header is to be trusted for user logins \\r\\n                (''''since ??.??'').\"\"\") \\r\\n\\r\\n    def authenticate(self, req):\\r\\n        if self.obey_remote_user_header and req.get_header(''Remote-User''): \\r\\n            return req.get_header(''Remote-User'') \\r\\n        return None\\r\\n\\r\\n}}}\\r\\n\\r\\nAdd this new parameter to your TracIni:\\r\\n{{{\\r\\n...\\r\\n[trac]\\r\\n...\\r\\nobey_remote_user_header = true\\r\\n...\\r\\n}}}\\r\\n\\r\\nRun tracd:\\r\\n{{{\\r\\ntracd -p 8101 -r -s proxified --base-path=/project/proxified\\r\\n}}}\\r\\n\\r\\nNote that if you want to install this plugin for all projects, you have to put it in your [TracPlugins#Plugindiscovery global plugins_dir] and enable it in your global trac.ini.\\r\\n\\r\\nGlobal config (e.g. `/srv/trac/conf/trac.ini`):\\r\\n{{{\\r\\n[components]\\r\\nremote-user-auth.* = enabled\\r\\n[inherit]\\r\\nplugins_dir = /srv/trac/plugins\\r\\n[trac]\\r\\nobey_remote_user_header = true\\r\\n}}}\\r\\n\\r\\nEnvironment config (e.g. `/srv/trac/envs/myenv`):\\r\\n{{{\\r\\n[inherit]\\r\\nfile = /srv/trac/conf/trac.ini\\r\\n}}}\\r\\n\\r\\n=== Serving a different base path than / ===\\r\\nTracd supports serving projects with different base urls than /<project>. The parameter name to change this is\\r\\n{{{\\r\\n $ tracd --base-path=/some/path\\r\\n}}}\\r\\n\\r\\n----\\r\\nSee also: TracInstall, TracCgi, TracModPython, TracGuide, [trac:TracOnWindowsStandalone#RunningTracdasservice Running tracd.exe as a Windows service]\\r\\n', NULL, NULL);\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('TracReports', 1, 1362994208285343, 'trac', '127.0.0.1', '= Trac Reports =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nThe Trac reports module provides a simple, yet powerful reporting facility\\r\\nto present information about tickets in the Trac database.\\r\\n\\r\\nRather than have its own report definition format, TracReports relies on standard SQL\\r\\n`SELECT` statements for custom report definition. \\r\\n\\r\\n  ''''''Note:'''''' ''''The report module is being phased out in its current form because it seriously limits the ability of the Trac team to make adjustments to the underlying database schema. We believe that the [wiki:TracQuery query module] is a good replacement that provides more flexibility and better usability. While there are certain reports that cannot yet be handled by the query module, we intend to further enhance it so that at some point the reports module can be completely removed. This also means that there will be no major enhancements to the report module anymore.''''\\r\\n\\r\\n  ''''You can already completely replace the reports module by the query module simply by disabling the former in [wiki:TracIni trac.ini]:''''\\r\\n  {{{\\r\\n  [components]\\r\\n  trac.ticket.report.* = disabled\\r\\n  }}}\\r\\n  ''''This will make the query module the default handler for the “View Tickets” navigation item. We encourage you to try this configuration and report back what kind of features of reports you are missing, if any.''''\\r\\n\\r\\nA report consists of these basic parts:\\r\\n * ''''''ID'''''' — Unique (sequential) identifier \\r\\n * ''''''Title'''''' — Descriptive title\\r\\n * ''''''Description'''''' — A brief description of the report, in WikiFormatting text.\\r\\n * ''''''Report Body'''''' — List of results from report query, formatted according to the methods described below.\\r\\n * ''''''Footer'''''' — Links to alternative download formats for this report.\\r\\n\\r\\n== Changing Sort Order ==\\r\\nSimple reports - ungrouped reports to be specific - can be changed to be sorted by any column simply by clicking the column header. \\r\\n\\r\\nIf a column header is a hyperlink (red), click the column you would like to sort by. Clicking the same header again reverses the order.\\r\\n\\r\\n== Changing Report Numbering ==\\r\\nThere may be instances where you need to change the ID of the report, perhaps to organize the reports better. At present this requires changes to the trac database. The ''''report'''' table has the following schema ''''(since 0.10)'''':\\r\\n * id integer PRIMARY KEY\\r\\n * author text\\r\\n * title text\\r\\n * query text\\r\\n * description text\\r\\nChanging the ID changes the shown order and number in the ''''Available Reports'''' list and the report''s perma-link. This is done by running something like:\\r\\n{{{\\r\\nupdate report set id=5 where id=3;\\r\\n}}}\\r\\nKeep in mind that the integrity has to be maintained (i.e., ID has to be unique, and you don''t want to exceed the max, since that''s managed by SQLite someplace).\\r\\n\\r\\nYou may also need to update or remove the report number stored in the report or query.\\r\\n\\r\\n== Navigating Tickets ==\\r\\nClicking on one of the report results will take you to that ticket. You can navigate through the results by clicking the ''''Next Ticket'''' or ''''Previous Ticket'''' links just below the main menu bar, or click the ''''Back to Report'''' link to return to the report page.\\r\\n\\r\\nYou can safely edit any of the tickets and continue to navigate through the results using the ''''!Next/Previous/Back to Report'''' links after saving your results, but when you return to the report, there will be no hint about what has changed, as would happen if you were navigating a list of tickets obtained from a query (see TracQuery#NavigatingTickets). ''''(since 0.11)''''\\r\\n\\r\\n== Alternative Download Formats ==\\r\\nAside from the default HTML view, reports can also be exported in a number of alternative formats.\\r\\nAt the bottom of the report page, you will find a list of available data formats. Click the desired link to \\r\\ndownload the alternative report format.\\r\\n\\r\\n=== Comma-delimited - CSV (Comma Separated Values) ===\\r\\nExport the report as plain text, each row on its own line, columns separated by a single comma ('','').\\r\\n''''''Note:'''''' The output is fully escaped so carriage returns, line feeds, and commas will be preserved in the output.\\r\\n\\r\\n=== Tab-delimited ===\\r\\nLike above, but uses tabs (\\\\t) instead of comma.\\r\\n\\r\\n=== RSS - XML Content Syndication ===\\r\\nAll reports support syndication using XML/RSS 2.0. To subscribe to an RSS feed, click the orange ''XML'' icon at the bottom of the page. See TracRss for general information on RSS support in Trac.\\r\\n\\r\\n----\\r\\n\\r\\n== Creating Custom Reports ==\\r\\n\\r\\n''''Creating a custom report requires a comfortable knowledge of SQL.''''\\r\\n\\r\\n''''''Note that you need to set up [TracPermissions#Reports permissions] in order to see the buttons for adding or editing reports.''''''\\r\\n\\r\\nA report is basically a single named SQL query, executed and presented by\\r\\nTrac.  Reports can be viewed and created from a custom SQL expression directly\\r\\nin the web interface.\\r\\n\\r\\nTypically, a report consists of a SELECT-expression from the ''ticket'' table,\\r\\nusing the available columns and sorting the way you want it.\\r\\n\\r\\n== Ticket columns ==\\r\\nThe ''''ticket'''' table has the following columns:\\r\\n * id\\r\\n * type\\r\\n * time\\r\\n * changetime\\r\\n * component\\r\\n * severity  \\r\\n * priority \\r\\n * owner\\r\\n * reporter\\r\\n * cc\\r\\n * version\\r\\n * milestone\\r\\n * status\\r\\n * resolution\\r\\n * summary\\r\\n * description\\r\\n * keywords\\r\\n\\r\\nSee TracTickets for a detailed description of the column fields.\\r\\n\\r\\nExample: ''''''All active tickets, sorted by priority and time''''''\\r\\n{{{\\r\\nSELECT id AS ticket, status, severity, priority, owner, \\r\\n       time AS created, summary FROM ticket \\r\\n  WHERE status IN (''new'', ''assigned'', ''reopened'')\\r\\n  ORDER BY priority, time\\r\\n}}}\\r\\n\\r\\n\\r\\n== Advanced Reports: Dynamic Variables ==\\r\\nFor more flexible reports, Trac supports the use of ''''dynamic variables'''' in report SQL statements. \\r\\nIn short, dynamic variables are ''''special'''' strings that are replaced by custom data before query execution.\\r\\n\\r\\n=== Using Variables in a Query ===\\r\\nThe syntax for dynamic variables is simple, any upper case word beginning with ''$'' is considered a variable.\\r\\n\\r\\nExample:\\r\\n{{{\\r\\nSELECT id AS ticket,summary FROM ticket WHERE priority=$PRIORITY\\r\\n}}}\\r\\n\\r\\nTo assign a value to $PRIORITY when viewing the report, you must define it as an argument in the report URL, leaving out the leading ''$''.\\r\\n\\r\\nExample:\\r\\n{{{\\r\\n http://trac.edgewall.org/reports/14?PRIORITY=high\\r\\n}}}\\r\\n\\r\\nTo use multiple variables, separate them with an ''&''.\\r\\n\\r\\nExample:\\r\\n{{{\\r\\n http://trac.edgewall.org/reports/14?PRIORITY=high&SEVERITY=critical\\r\\n}}}\\r\\n\\r\\nDynamic variables can also be used in the report title and description (since 1.1.1).\\r\\n\\r\\n=== !Special/Constant Variables ===\\r\\nThere is one dynamic variable whose value is set automatically (the URL does not have to be changed) to allow practical reports. \\r\\n\\r\\n * $USER — Username of logged in user.\\r\\n\\r\\nExample (''''List all tickets assigned to me''''):\\r\\n{{{\\r\\nSELECT id AS ticket,summary FROM ticket WHERE owner=$USER\\r\\n}}}\\r\\n\\r\\n\\r\\n\\r\\n== Advanced Reports: Custom Formatting ==\\r\\nTrac is also capable of more advanced reports, including custom layouts,\\r\\nresult grouping and user-defined CSS styles. To create such reports, we''ll use\\r\\nspecialized SQL statements to control the output of the Trac report engine.\\r\\n\\r\\n=== Special Columns ===\\r\\nTo format reports, TracReports looks for ''magic'' column names in the query\\r\\nresult. These ''magic'' names are processed and affect the layout and style of the \\r\\nfinal report.\\r\\n\\r\\n=== Automatically formatted columns ===\\r\\n * ''''''ticket'''''' — Ticket ID number. Becomes a hyperlink to that ticket. \\r\\n * ''''''id'''''' — same as ''''''ticket'''''' above when ''''''realm'''''' is not set\\r\\n * ''''''realm'''''' — together with ''''''id'''''', can be used to create links to other resources than tickets (e.g. a realm of ''''wiki'''' and an ''''id'''' to a page name will create a link to that wiki page)\\r\\n * ''''''created, modified, date, time'''''' — Format cell as a date and/or time.\\r\\n * ''''''description'''''' — Ticket description field, parsed through the wiki engine.\\r\\n\\r\\n''''''Example:''''''\\r\\n{{{\\r\\nSELECT id AS ticket, created, status, summary FROM ticket \\r\\n}}}\\r\\n\\r\\nThose columns can also be defined but marked as hidden, see [#column-syntax below].\\r\\n\\r\\nSee trac:wiki/CookBook/Configuration/Reports for some example of creating reports for realms other than ''''ticket''''.\\r\\n\\r\\n=== Custom formatting columns ===\\r\\nColumns whose names begin and end with 2 underscores (Example: ''''''`__color__`'''''') are\\r\\nassumed to be ''''formatting hints'''', affecting the appearance of the row.\\r\\n \\r\\n * ''''''`__group__`'''''' — Group results based on values in this column. Each group will have its own header and table.\\r\\n * ''''''`__grouplink__`'''''' — Make the header of each group a link to the specified URL. The URL is taken from the first row of each group.\\r\\n * ''''''`__color__`'''''' — Should be a numeric value ranging from 1 to 5 to select a pre-defined row color. Typically used to color rows by issue priority.\\r\\n{{{\\r\\n#!html\\r\\n<div style=\"margin-left:7.5em\">Defaults: \\r\\n<span style=\"border: none; color: #333; background: transparent;  font-size: 85%; background: #fdc; border-color: #e88; color: #a22\">Color 1</span>\\r\\n<span style=\"border: none; color: #333; background: transparent;  font-size: 85%; background: #ffb; border-color: #eea; color: #880\">Color 2</span>\\r\\n<span style=\"border: none; color: #333; background: transparent;  font-size: 85%; background: #fbfbfb; border-color: #ddd; color: #444\">Color 3</span>\\r\\n<span style=\"border: none; color: #333; background: transparent; font-size: 85%; background: #e7ffff; border-color: #cee; color: #099\">Color 4</span>\\r\\n<span style=\"border: none; color: #333; background: transparent;  font-size: 85%; background: #e7eeff; border-color: #cde; color: #469\">Color 5</span>\\r\\n</div>\\r\\n}}}\\r\\n * ''''''`__style__`'''''' — A custom CSS style expression to use on the `<tr>` element of the current row.\\r\\n * ''''''`__class__`'''''' — Zero or more space-separated CSS class names to be set on the `<tr>` element of the current row. These classes are added to the class name derived from `__color__` and the odd / even indicator.\\r\\n\\r\\n''''''Example:'''''' ''''List active tickets, grouped by milestone, group header linked to milestone page, colored by priority''''\\r\\n{{{\\r\\nSELECT p.value AS __color__,\\r\\n     t.milestone AS __group__,\\r\\n     ''../milestone/'' || t.milestone AS __grouplink__,\\r\\n     (CASE owner WHEN ''daniel'' THEN ''font-weight: bold; background: red;'' ELSE '''' END) AS __style__,\\r\\n       t.id AS ticket, summary\\r\\n  FROM ticket t,enum p\\r\\n  WHERE t.status IN (''new'', ''assigned'', ''reopened'') \\r\\n    AND p.name=t.priority AND p.type=''priority''\\r\\n  ORDER BY t.milestone, p.value, t.severity, t.time\\r\\n}}}\\r\\n\\r\\n''''''Note:'''''' A table join is used to match ''''ticket'''' priorities with their\\r\\nnumeric representation from the ''''enum'''' table.\\r\\n\\r\\n=== Changing layout of report rows === #column-syntax\\r\\nBy default, all columns on each row are display on a single row in the HTML\\r\\nreport, possibly formatted according to the descriptions above. However, it''s\\r\\nalso possible to create multi-line report entries.\\r\\n\\r\\n * ''''''`column_`'''''' — ''''Break row after this''''. By appending an underscore (''_'') to the column name, the remaining columns will be continued on a second line.\\r\\n\\r\\n * ''''''`_column_`'''''' — ''''Full row''''. By adding an underscore (''_'') both at the beginning and the end of a column name, the data will be shown on a separate row.\\r\\n\\r\\n * ''''''`_column`'''''' — ''''Hide data''''. Prepending an underscore (''_'') to a column name instructs Trac to hide the contents from the HTML output. This is useful for information to be visible only if downloaded in other formats (like CSV or RSS/XML).\\r\\n   This can be used to hide any kind of column, even important ones required for identifying the resource, e.g. `id as _id` will hide the ''''''Id'''''' column but the link to the ticket will be present.\\r\\n\\r\\n''''''Example:'''''' ''''List active tickets, grouped by milestone, colored by priority, with  description and multi-line layout''''\\r\\n\\r\\n{{{\\r\\nSELECT p.value AS __color__,\\r\\n       t.milestone AS __group__,\\r\\n       (CASE owner \\r\\n          WHEN ''daniel'' THEN ''font-weight: bold; background: red;'' \\r\\n          ELSE '''' END) AS __style__,\\r\\n       t.id AS ticket, summary AS summary_,             -- ## Break line here\\r\\n       component,version, severity, milestone, status, owner,\\r\\n       time AS created, changetime AS modified,         -- ## Dates are formatted\\r\\n       description AS _description_,                    -- ## Uses a full row\\r\\n       changetime AS _changetime, reporter AS _reporter -- ## Hidden from HTML output\\r\\n  FROM ticket t,enum p\\r\\n  WHERE t.status IN (''new'', ''assigned'', ''reopened'') \\r\\n    AND p.name=t.priority AND p.type=''priority''\\r\\n  ORDER BY t.milestone, p.value, t.severity, t.time\\r\\n}}}\\r\\n\\r\\n=== Reporting on custom fields ===\\r\\n\\r\\nIf you have added custom fields to your tickets (a feature since v0.8, see TracTicketsCustomFields), you can write a SQL query to cover them. You''ll need to make a join on the ticket_custom table, but this isn''t especially easy.\\r\\n\\r\\nIf you have tickets in the database ''''before'''' you declare the extra fields in trac.ini, there will be no associated data in the ticket_custom table. To get around this, use SQL''s \"LEFT OUTER JOIN\" clauses. See [trac:TracIniReportCustomFieldSample TracIniReportCustomFieldSample] for some examples.\\r\\n\\r\\n=== A note about SQL rewriting #rewriting\\r\\n\\r\\nBeyond the relatively trivial replacement of dynamic variables, the SQL query is also altered in order to support two features of the reports:\\r\\n 1. [#sort-order changing the sort order]\\r\\n 2. pagination support (limitation of the number of result rows displayed on each page)\\r\\nIn order to support the first feature, the sort column is inserted in the `ORDER BY` clause in the first position or in the second position if a `__group__` column is specified (an `ORDER BY` clause is created if needed). In order to support pagination, a `LIMIT ... OFFSET ...` clause is appended.\\r\\nThe query might be too complex for the automatic rewrite to work correctly, resulting in an erroneous query. In this case you still have the possibility to control exactly how the rewrite is done by manually inserting the following tokens:\\r\\n - `@SORT_COLUMN@`, the place where the name of the selected sort column will be inserted,\\r\\n - `@LIMIT_OFFSET@`, the place where the pagination support clause will be added\\r\\nNote that if you write them after an SQL comment, `--`, you''ll effectively disable rewriting if this is what you want!\\r\\n\\r\\nLet''s take an example, consider the following SQL query:\\r\\n{{{\\r\\n-- ## 4: Assigned, Active Tickets by Owner ## --\\r\\n\\r\\n-- \\r\\n-- List assigned tickets, group by ticket owner, sorted by priority.\\r\\n-- \\r\\n\\r\\nSELECT p.value AS __color__,\\r\\n   owner AS __group__,\\r\\n   id AS ticket, summary, component, milestone, t.type AS type, severity, time AS created,\\r\\n   changetime AS _changetime, description AS _description,\\r\\n   reporter AS _reporter\\r\\n  FROM ticket t,enum p\\r\\n  WHERE status = ''assigned''\\r\\nAND p.name=t.priority AND p.type=''priority''\\r\\n  ORDER BY __group__, p.value, severity, time\\r\\n}}}\\r\\n\\r\\nThe automatic rewrite will be the following (4 rows per page, page 2, sorted by `component`):\\r\\n{{{\\r\\nSELECT p.value AS __color__,\\r\\n   owner AS __group__,\\r\\n   id AS ticket, summary, component, milestone, t.type AS type, severity, time AS created,\\r\\n   changetime AS _changetime, description AS _description,\\r\\n   reporter AS _reporter\\r\\n  FROM ticket t,enum p\\r\\n  WHERE status = ''assigned''\\r\\nAND p.name=t.priority AND p.type=''priority''\\r\\n  ORDER BY __group__ ASC, `component` ASC,  __group__, p.value, severity, time\\r\\n LIMIT 4 OFFSET 4\\r\\n}}}\\r\\n\\r\\nThe equivalent SQL query with the rewrite tokens would have been:\\r\\n{{{\\r\\nSELECT p.value AS __color__,\\r\\n   owner AS __group__,\\r\\n   id AS ticket, summary, component, milestone, t.type AS type, severity, time AS created,\\r\\n   changetime AS _changetime, description AS _description,\\r\\n   reporter AS _reporter\\r\\n  FROM ticket t,enum p\\r\\n  WHERE status = ''assigned''\\r\\nAND p.name=t.priority AND p.type=''priority''\\r\\n  ORDER BY __group__, @SORT_COLUMN@, p.value, severity, time\\r\\n@LIMIT_OFFSET@\\r\\n}}}\\r\\n\\r\\nIf you want to always sort first by priority and only then by the user selected sort column, simply use the following `ORDER BY` clause:\\r\\n{{{\\r\\n  ORDER BY __group__, p.value, @SORT_COLUMN@, severity, time\\r\\n}}}\\r\\n\\r\\n----\\r\\nSee also: TracTickets, TracQuery, TracGuide, [http://www.sqlite.org/lang_expr.html Query Language Understood by SQLite]\\r\\n', NULL, NULL),\n('TracFastCgi', 1, 1362994208288223, 'trac', '127.0.0.1', '[[PageOutline]]\\r\\n\\r\\n= Trac with FastCGI =\\r\\n\\r\\n[http://www.fastcgi.com/ FastCGI] interface allows Trac to remain resident much like with [wiki:TracModPython mod_python] or [wiki:TracModWSGI mod_wsgi]. It is faster than external CGI interfaces which must start a new process for each request.  Additionally, it is supported by much wider variety of web servers.\\r\\n\\r\\nNote that unlike mod_python, FastCGI supports [http://httpd.apache.org/docs/suexec.html Apache SuEXEC], i.e. run with different permissions than web server running with (`mod_wsgi` supports the `WSGIDaemonProcess` with user / group parameters to achieve the same effect).\\r\\n\\r\\n''''''Note for Windows:'''''' Trac''s FastCGI does not run under Windows, as Windows does not implement `Socket.fromfd`, which is used by `_fcgi.py`. If you want to connect to IIS, you may want to try [trac:TracOnWindowsIisAjp AJP]/[trac:TracOnWindowsIisAjp ISAPI].\\r\\n\\r\\n[[PageOutline(2-3,Overview,inline)]]\\r\\n\\r\\n\\r\\n== Simple Apache configuration ==\\r\\n\\r\\nThere are two FastCGI modules commonly available for Apache: `mod_fastcgi` and\\r\\n`mod_fcgid` (preferred). The latter is more up-to-date.\\r\\n\\r\\nThe following sections focus on the FCGI specific setup, see also [wiki:TracModWSGI#ConfiguringAuthentication] for configuring the authentication in Apache.\\r\\n\\r\\nRegardless of which cgi module is used, be sure the web server has executable permissions on the cgi-bin folder. While FastCGI will throw specific permissions errors, mod_fcgid will throw an ambiguous error if this has not been done. (Connection reset by peer: mod_fcgid: error reading data from FastCGI server) \\r\\n\\r\\n=== Set up with `mod_fastcgi` ===\\r\\n`mod_fastcgi` uses `FastCgiIpcDir` and `FastCgiConfig` directives that should be added to an appropriate Apache configuration file:\\r\\n{{{\\r\\n# Enable fastcgi for .fcgi files\\r\\n# (If you''re using a distro package for mod_fcgi, something like\\r\\n# this is probably already present)\\r\\n<IfModule mod_fastcgi.c>\\r\\n   AddHandler fastcgi-script .fcgi\\r\\n   FastCgiIpcDir /var/lib/apache2/fastcgi \\r\\n</IfModule>\\r\\nLoadModule fastcgi_module /usr/lib/apache2/modules/mod_fastcgi.so\\r\\n}}}\\r\\nSetting `FastCgiIpcDir` is optional if the default is suitable. Note that the `LoadModule` line must be after the `IfModule` group.\\r\\n\\r\\nConfigure `ScriptAlias` or similar options as described in TracCgi, but\\r\\ncalling `trac.fcgi` instead of `trac.cgi`.\\r\\n\\r\\nAdd the following to the Apache configuration file (below the `FastCgiIpcDir` line) if you intend to set up the `TRAC_ENV` as an overall default:\\r\\n{{{\\r\\nFastCgiConfig -initial-env TRAC_ENV=/path/to/env/trac\\r\\n}}}\\r\\n\\r\\nAlternatively, you can serve multiple Trac projects in a directory by adding this:\\r\\n{{{\\r\\nFastCgiConfig -initial-env TRAC_ENV_PARENT_DIR=/parent/dir/of/projects\\r\\n}}}\\r\\n\\r\\n=== Set up with `mod_fcgid` ===\\r\\nConfigure `ScriptAlias` (see TracCgi for details), but call `trac.fcgi`\\r\\ninstead of `trac.cgi`. Note that slash at the end - it is important.\\r\\n{{{\\r\\nScriptAlias /trac /path/to/www/trac/cgi-bin/trac.fcgi/\\r\\n}}}\\r\\n\\r\\nTo set up Trac environment for `mod_fcgid` it is necessary to use\\r\\n`DefaultInitEnv` directive. It cannot be used in `Directory` or\\r\\n`Location` context, so if you need to support multiple projects, try\\r\\nalternative environment setup below.\\r\\n\\r\\n{{{\\r\\nDefaultInitEnv TRAC_ENV /path/to/env/trac/\\r\\n}}}\\r\\n\\r\\n=== alternative environment setup ===\\r\\nA better method to specify path to Trac environment is to embed the path\\r\\ninto `trac.fcgi` script itself. That doesn''t require configuration of server\\r\\nenvironment variables, works for both FastCgi modules\\r\\n(and for [http://www.lighttpd.net/ lighttpd] and CGI as well):\\r\\n{{{\\r\\nimport os\\r\\nos.environ[''TRAC_ENV''] = \"/path/to/projectenv\"\\r\\n}}}\\r\\nor\\r\\n{{{\\r\\nimport os\\r\\nos.environ[''TRAC_ENV_PARENT_DIR''] = \"/path/to/project/parent/dir\"\\r\\n}}}\\r\\n\\r\\nWith this method different projects can be supported by using different\\r\\n`.fcgi` scripts with different `ScriptAliases`.\\r\\n\\r\\nSee [https://coderanger.net/~coderanger/httpd/fcgi_example.conf this fcgid example config] which uses a !ScriptAlias directive with trac.fcgi with a trailing / like this:\\r\\n{{{\\r\\nScriptAlias / /srv/tracsite/cgi-bin/trac.fcgi/\\r\\n}}}\\r\\n\\r\\n== Simple Cherokee Configuration ==\\r\\n\\r\\nThe configuration on Cherokee''s side is quite simple. You will only need to know that you can spawn Trac as an SCGI process.\\r\\nYou can either start it manually, or better yet, automatically by letting Cherokee spawn the server whenever it is down.\\r\\nFirst set up an information source in cherokee-admin with a local interpreter.\\r\\n\\r\\n{{{\\r\\nHost:\\r\\nlocalhost:4433\\r\\n\\r\\nInterpreter:\\r\\n/usr/bin/tracd —single-env —daemonize —protocol=scgi —hostname=localhost —port=4433 /path/to/project/\\r\\n}}}\\r\\n\\r\\nIf the port was not reachable, the interpreter command would be launched. Note that, in the definition of the information source, you will have to manually launch the spawner if you use a ''''Remote host'''' as ''''Information source'''' instead of a ''''Local interpreter''''.\\r\\n\\r\\nAfter doing this, we will just have to create a new rule managed by the SCGI handler to access Trac. It can be created in a new virtual server, trac.example.net for instance, and will only need two rules. The ''''''default'''''' one will use the SCGI handler associated to the previously created information source.\\r\\nThe second rule will be there to serve the few static files needed to correctly display the Trac interface. Create it as ''''Directory rule'''' for ''''/common'''' and just set it to the ''''Static files'''' handler and with a ''''Document root'''' that points to the appropriate files: ''''$TRAC_LOCAL/htdocs/'''' (where $TRAC_LOCAL is a directory defined by the user or the system administrator to place local trac resources).\\r\\n\\r\\nNote:\\\\\\\\\\r\\nIf the tracd process fails to start up, and cherokee displays a 503 error page, you might be missing the [http://trac.saddi.com/flup python-flup] package.\\\\\\\\\\r\\nPython-flup is a dependency which provides trac with SCGI capability. You can install it on debian based systems with:\\r\\n{{{\\r\\nsudo apt-get install python-flup\\r\\n}}}\\r\\n\\r\\n\\r\\n== Simple Lighttpd Configuration ==\\r\\n\\r\\nThe FastCGI front-end was developed primarily for use with alternative webservers, such as [http://www.lighttpd.net/ lighttpd].\\r\\n\\r\\nlighttpd is a secure, fast, compliant and very flexible web-server that has been optimized for high-performance\\r\\nenvironments.  It has a very low memory footprint compared to other web servers and takes care of CPU load.\\r\\n\\r\\nFor using `trac.fcgi`(prior to 0.11) / fcgi_frontend.py (0.11) with lighttpd add the following to your lighttpd.conf:\\r\\n{{{\\r\\n#var.fcgi_binary=\"/usr/bin/python /path/to/fcgi_frontend.py\" # 0.11 if installed with easy_setup, it is inside the egg directory\\r\\nvar.fcgi_binary=\"/path/to/cgi-bin/trac.fcgi\" # 0.10 name of prior fcgi executable\\r\\nfastcgi.server = (\"/trac\" =>\\r\\n   \\r\\n                   (\"trac\" =>\\r\\n                     (\"socket\" => \"/tmp/trac-fastcgi.sock\",\\r\\n                      \"bin-path\" => fcgi_binary,\\r\\n                      \"check-local\" => \"disable\",\\r\\n                      \"bin-environment\" =>\\r\\n                        (\"TRAC_ENV\" => \"/path/to/projenv\")\\r\\n                     )\\r\\n                   )\\r\\n                 )\\r\\n}}}\\r\\n\\r\\nNote that you will need to add a new entry to `fastcgi.server` for each separate Trac instance that you wish to run. Alternatively, you may use the `TRAC_ENV_PARENT_DIR` variable instead of `TRAC_ENV` as described above,\\r\\nand you may set one of the two in `trac.fcgi` instead of in `lighttpd.conf`\\r\\nusing `bin-environment` (as in the section above on Apache configuration).\\r\\n\\r\\nNote that lighttpd has a bug related to ''SCRIPT_NAME'' and ''PATH_INFO'' when the uri of fastcgi.server is ''/'' instead of ''/trac'' in this example (see [trac:#2418]). This is fixed in lighttpd 1.5, and under lighttpd 1.4.23 or later the workaround is to add `\"fix-root-scriptname\" => \"enable\"` as a parameter of fastcgi.server.\\r\\n\\r\\nFor using two projects with lighttpd add the following to your `lighttpd.conf`:\\r\\n{{{\\r\\nfastcgi.server = (\"/first\" =>\\r\\n                   (\"first\" =>\\r\\n                    (\"socket\" => \"/tmp/trac-fastcgi-first.sock\",\\r\\n                     \"bin-path\" => fcgi_binary,\\r\\n                     \"check-local\" => \"disable\",\\r\\n                     \"bin-environment\" =>\\r\\n                       (\"TRAC_ENV\" => \"/path/to/projenv-first\")\\r\\n                    )\\r\\n                  ),\\r\\n                  \"/second\" =>\\r\\n                    (\"second\" =>\\r\\n                    (\"socket\" => \"/tmp/trac-fastcgi-second.sock\",\\r\\n                     \"bin-path\" => fcgi_binary,\\r\\n                     \"check-local\" => \"disable\",\\r\\n                     \"bin-environment\" =>\\r\\n                       (\"TRAC_ENV\" => \"/path/to/projenv-second\")\\r\\n                    )\\r\\n                  )\\r\\n                )\\r\\n}}}\\r\\nNote that field values are different.  If you prefer setting the environment\\r\\nvariables in the `.fcgi` scripts, then copy/rename `trac.fcgi`, e.g., to\\r\\n`first.fcgi` and `second.fcgi`, and reference them in the above settings.\\r\\nNote that the above will result in different processes in any event, even\\r\\nif both are running from the same `trac.fcgi` script.\\r\\n\\r\\n{{{\\r\\n#!div class=important\\r\\n''''''Note'''''' It''s very important the order on which server.modules are loaded, if mod_auth is not loaded ''''''BEFORE'''''' mod_fastcgi, then the server will fail to authenticate the user.\\r\\n}}}\\r\\n\\r\\nFor authentication you should enable mod_auth in lighttpd.conf ''server.modules'', select auth.backend and auth rules:\\r\\n{{{\\r\\nserver.modules              = (\\r\\n...\\r\\n  \"mod_auth\",\\r\\n...\\r\\n)\\r\\n\\r\\nauth.backend               = \"htpasswd\"\\r\\n\\r\\n# Separated password files for each project\\r\\n# See \"Conditional Configuration\" in\\r\\n# http://trac.lighttpd.net/trac/file/branches/lighttpd-merge-1.4.x/doc/configuration.txt\\r\\n\\r\\n$HTTP[\"url\"] =~ \"^/first/\" {\\r\\n  auth.backend.htpasswd.userfile = \"/path/to/projenv-first/htpasswd.htaccess\"\\r\\n}\\r\\n$HTTP[\"url\"] =~ \"^/second/\" {\\r\\n  auth.backend.htpasswd.userfile = \"/path/to/projenv-second/htpasswd.htaccess\"\\r\\n}\\r\\n\\r\\n# Enable auth on trac URLs, see\\r\\n# http://trac.lighttpd.net/trac/file/branches/lighttpd-merge-1.4.x/doc/authentication.txt\\r\\n\\r\\nauth.require = (\"/first/login\" =>\\r\\n                (\"method\"  => \"basic\",\\r\\n                 \"realm\"   => \"First project\",\\r\\n                 \"require\" => \"valid-user\"\\r\\n                ),\\r\\n                \"/second/login\" =>\\r\\n                (\"method\"  => \"basic\",\\r\\n                 \"realm\"   => \"Second project\",\\r\\n                 \"require\" => \"valid-user\"\\r\\n                )\\r\\n               )\\r\\n\\r\\n\\r\\n}}}\\r\\nNote that lighttpd (I use version 1.4.3) stopped if password file doesn''t exist.\\r\\n\\r\\nNote that lighttpd doesn''t support ''valid-user'' in versions prior to 1.3.16.\\r\\n\\r\\nConditional configuration is also useful for mapping static resources, i.e. serving out images and CSS directly instead of through FastCGI:\\r\\n{{{\\r\\n# Aliasing functionality is needed\\r\\nserver.modules += (\"mod_alias\")\\r\\n\\r\\n# Set up an alias for the static resources\\r\\nalias.url = (\"/trac/chrome/common\" => \"/usr/share/trac/htdocs\")\\r\\n\\r\\n# Use negative lookahead, matching all requests that ask for any resource under /trac, EXCEPT in\\r\\n# /trac/chrome/common, and use FastCGI for those\\r\\n$HTTP[\"url\"] =~ \"^/trac(?!/chrome/common)\" {\\r\\n# Even if you have other fastcgi.server declarations for applications other than Trac, do NOT use += here\\r\\nfastcgi.server = (\"/trac\" =>\\r\\n                   (\"trac\" =>\\r\\n                     (\"socket\" => \"/tmp/trac-fastcgi.sock\",\\r\\n                      \"bin-path\" => fcgi_binary,\\r\\n                      \"check-local\" => \"disable\",\\r\\n                      \"bin-environment\" =>\\r\\n                        (\"TRAC_ENV\" => \"/path/to/projenv\")\\r\\n                     )\\r\\n                   )\\r\\n                 )\\r\\n}\\r\\n}}}\\r\\nThe technique can be easily adapted for use with multiple projects by creating aliases for each of them, and wrapping the fastcgi.server declarations inside conditional configuration blocks.\\r\\nAlso there is another way to handle multiple projects and it''s to use TRAC_ENV_PARENT_DIR instead of TRAC_ENV and use global auth, let''s see an example:\\r\\n{{{\\r\\n#  This is for handling multiple projects\\r\\n  alias.url       = ( \"/trac/\" => \"/path/to/trac/htdocs/\" )\\r\\n\\r\\n  fastcgi.server += (\"/projects\"  =>\\r\\n                      (\"trac\" =>\\r\\n                        (\\r\\n                          \"socket\" => \"/tmp/trac.sock\",\\r\\n                          \"bin-path\" => fcgi_binary,\\r\\n                          \"check-local\" => \"disable\",\\r\\n                          \"bin-environment\" =>\\r\\n                            (\"TRAC_ENV_PARENT_DIR\" => \"/path/to/parent/dir/of/projects/\" )\\r\\n                        )\\r\\n                      )\\r\\n                    )\\r\\n#And here starts the global auth configuration\\r\\n  auth.backend = \"htpasswd\"\\r\\n  auth.backend.htpasswd.userfile = \"/path/to/unique/htpassword/file/trac.htpasswd\"\\r\\n  $HTTP[\"url\"] =~ \"^/projects/.*/login$\" {\\r\\n    auth.require = (\"/\" =>\\r\\n                     (\\r\\n                       \"method\"  => \"basic\",\\r\\n                       \"realm\"   => \"trac\",\\r\\n                       \"require\" => \"valid-user\"\\r\\n                     )\\r\\n                   )\\r\\n  }\\r\\n}}}\\r\\n\\r\\nChanging date/time format also supported by lighttpd over environment variable LC_TIME\\r\\n{{{\\r\\nfastcgi.server = (\"/trac\" =>\\r\\n                   (\"trac\" =>\\r\\n                     (\"socket\" => \"/tmp/trac-fastcgi.sock\",\\r\\n                      \"bin-path\" => fcgi_binary,\\r\\n                      \"check-local\" => \"disable\",\\r\\n                      \"bin-environment\" =>\\r\\n                        (\"TRAC_ENV\" => \"/path/to/projenv\",\\r\\n                        \"LC_TIME\" => \"ru_RU\")\\r\\n                     )\\r\\n                   )\\r\\n                 )\\r\\n}}}\\r\\nFor details about languages specification see [trac:TracFaq TracFaq] question 2.13.\\r\\n\\r\\nOther important information like the [wiki:TracInstall#MappingStaticResources mapping static resources advices] are useful for non-fastcgi specific installation aspects.\\r\\n]\\r\\n\\r\\nRelaunch lighttpd, and browse to `http://yourhost.example.org/trac` to access Trac.\\r\\n\\r\\nNote about running lighttpd with reduced permissions:\\r\\n\\r\\nIf nothing else helps and trac.fcgi doesn''t start with lighttpd settings `server.username = \"www-data\"`, `server.groupname = \"www-data\"`, then in the `bin-environment` section set `PYTHON_EGG_CACHE` to the home directory of `www-data` or some other directory accessible to this account for writing.\\r\\n\\r\\n\\r\\n== Simple !LiteSpeed Configuration ==\\r\\n\\r\\nThe FastCGI front-end was developed primarily for use with alternative webservers, such as [http://www.litespeedtech.com/ LiteSpeed].\\r\\n\\r\\n!LiteSpeed web server is an event-driven asynchronous Apache replacement designed from the ground-up to be secure, scalable, and operate with minimal resources. !LiteSpeed can operate directly from an Apache config file and is targeted for business-critical environments.\\r\\n\\r\\n 1. Please make sure you have first have a working install of a Trac project. Test install with “tracd” first.\\r\\n\\r\\n 2. Create a Virtual Host for this setup. From now on we will refer to this vhost as !TracVhost. For this tutorial we will be assuming that your trac project will be accessible via:\\r\\n\\r\\n{{{\\r\\nhttp://yourdomain.com/trac/\\r\\n}}}\\r\\n\\r\\n 3. Go “!TracVhost → External Apps” tab and create a new “External Application”.\\r\\n\\r\\n{{{\\r\\nName: MyTracFCGI\t\\r\\nAddress: uds://tmp/lshttpd/mytracfcgi.sock\\r\\nMax Connections: 10\\r\\nEnvironment: TRAC_ENV=/fullpathto/mytracproject/ <--- path to root folder of trac project\\r\\nInitial Request Timeout (secs): 30\\r\\nRetry Timeout (secs): 0\\r\\nPersistent Connection\tYes\\r\\nConnection Keepalive Timeout: 30\\r\\nResponse Bufferring: No\t\\r\\nAuto Start: Yes\\r\\nCommand: /usr/share/trac/cgi-bin/trac.fcgi  <--- path to trac.fcgi\\r\\nBack Log: 50\\r\\nInstances: 10\\r\\n}}}\\r\\n\\r\\n 4. Optional. If you need to use htpasswd based authentication. Go to “!TracVhost → Security” tab and create a new security “Realm”.\\r\\n\\r\\n{{{\\r\\nDB Type: Password File\\r\\nRealm Name: MyTracUserDB               <--- any name you wish and referenced later\\r\\nUser DB Location: /fullpathto/htpasswd <--- path to your htpasswd file\\r\\n}}}\\r\\n\\r\\nIf you don’t have a htpasswd file or don’t know how to create the entries within one, go to http://sherylcanter.com/encrypt.php, to generate the user:password combos.\\r\\n\\r\\n 5. Go to “!PythonVhost → Contexts” and create a new “FCGI Context”.\\r\\n\\r\\n{{{\\r\\nURI: /trac/                              <--- URI path to bind to python fcgi app we created\t\\r\\nFast CGI App: [VHost Level] MyTractFCGI  <--- select the trac fcgi extapp we just created\\r\\nRealm: TracUserDB                        <--- only if (4) is set. select realm created in (4)\\r\\n}}}\\r\\n\\r\\n 6. Modify `/fullpathto/mytracproject/conf/trac.ini`\\r\\n\\r\\n{{{\\r\\n#find/set base_rul, url, and link variables\\r\\nbase_url = http://yourdomain.com/trac/ <--- base url to generate correct links to\\r\\nurl = http://yourdomain.com/trac/      <--- link of project\\r\\nlink = http://yourdomain.com/trac/     <--- link of graphic logo\\r\\n}}}\\r\\n\\r\\n 7. Restart !LiteSpeed, “lswsctrl restart”, and access your new Trac project at: \\r\\n\\r\\n{{{\\r\\nhttp://yourdomain.com/trac/\\r\\n}}}\\r\\n\\r\\n\\r\\n== Simple Nginx Configuration ==\\r\\n\\r\\nNginx is able to communicate with FastCGI processes, but can not spawn them. So you need to start FastCGI server for Trac separately.\\r\\n\\r\\n 1. Nginx configuration with basic authentication handled by Nginx - confirmed to work on 0.6.32\\r\\n{{{\\r\\n    server {\\r\\n        listen       10.9.8.7:443;\\r\\n        server_name  trac.example;\\r\\n\\r\\n        ssl                  on;\\r\\n        ssl_certificate      /etc/ssl/trac.example.crt;\\r\\n        ssl_certificate_key  /etc/ssl/trac.example.key;\\r\\n\\r\\n        ssl_session_timeout  5m;\\r\\n\\r\\n        ssl_protocols  SSLv2 SSLv3 TLSv1;\\r\\n        ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;\\r\\n        ssl_prefer_server_ciphers   on;\\r\\n\\r\\n        # (Or ``^/some/prefix/(.*)``.\\r\\n        if ($uri ~ ^/(.*)) {\\r\\n             set $path_info /$1;\\r\\n        }\\r\\n\\r\\n        # it makes sense to serve static resources through Nginx\\r\\n        location /chrome/ {\\r\\n             alias /home/trac/instance/static/htdocs/;\\r\\n        }\\r\\n\\r\\n        # You can copy this whole location to ``location [/some/prefix]/login``\\r\\n        # and remove the auth entries below if you want Trac to enforce\\r\\n        # authorization where appropriate instead of needing to authenticate\\r\\n        # for accessing the whole site.\\r\\n        # (Or ``location /some/prefix``.)\\r\\n        location / {\\r\\n            auth_basic            \"trac realm\";\\r\\n            auth_basic_user_file /home/trac/htpasswd;\\r\\n\\r\\n            # socket address\\r\\n            fastcgi_pass   unix:/home/trac/run/instance.sock;\\r\\n\\r\\n            # python - wsgi specific\\r\\n            fastcgi_param HTTPS on;\\r\\n\\r\\n            ## WSGI REQUIRED VARIABLES\\r\\n            # WSGI application name - trac instance prefix.\\r\\n\t    # (Or ``fastcgi_param  SCRIPT_NAME  /some/prefix``.)\\r\\n            fastcgi_param  SCRIPT_NAME        \"\";\\r\\n            fastcgi_param  PATH_INFO          $path_info;\\r\\n\\r\\n            ## WSGI NEEDED VARIABLES - trac warns about them\\r\\n            fastcgi_param  REQUEST_METHOD     $request_method;\\r\\n            fastcgi_param  SERVER_NAME        $server_name;\\r\\n            fastcgi_param  SERVER_PORT        $server_port;\\r\\n            fastcgi_param  SERVER_PROTOCOL    $server_protocol;\\r\\n            fastcgi_param  QUERY_STRING       $query_string;\\r\\n\\r\\n            # For Nginx authentication to work - do not forget to comment these\\r\\n            # lines if not using Nginx for authentication\\r\\n            fastcgi_param  AUTH_USER          $remote_user;\\r\\n            fastcgi_param  REMOTE_USER        $remote_user;\\r\\n\\r\\n            # for ip to work\\r\\n            fastcgi_param REMOTE_ADDR         $remote_addr;\\r\\n\\r\\n            # For attchments to work\\r\\n            fastcgi_param    CONTENT_TYPE     $content_type;\\r\\n            fastcgi_param    CONTENT_LENGTH   $content_length;\\r\\n        }\\r\\n    }\\r\\n}}}\\r\\n\\r\\n 2. Modified trac.fcgi:\\r\\n\\r\\n{{{\\r\\n#!/usr/bin/env python\\r\\nimport os\\r\\nsockaddr = ''/home/trac/run/instance.sock''\\r\\nos.environ[''TRAC_ENV''] = ''/home/trac/instance''\\r\\n\\r\\ntry:\\r\\n     from trac.web.main import dispatch_request\\r\\n     import trac.web._fcgi\\r\\n\\r\\n     fcgiserv = trac.web._fcgi.WSGIServer(dispatch_request, \\r\\n          bindAddress = sockaddr, umask = 7)\\r\\n     fcgiserv.run()\\r\\n\\r\\nexcept SystemExit:\\r\\n    raise\\r\\nexcept Exception, e:\\r\\n    print ''Content-Type: text/plain\\\\r\\\\n\\\\r\\\\n'',\\r\\n    print ''Oops...''\\r\\n    print\\r\\n    print ''Trac detected an internal error:''\\r\\n    print\\r\\n    print e\\r\\n    print\\r\\n    import traceback\\r\\n    import StringIO\\r\\n    tb = StringIO.StringIO()\\r\\n    traceback.print_exc(file=tb)\\r\\n    print tb.getvalue()\\r\\n\\r\\n}}}\\r\\n\\r\\n 3. reload nginx and launch trac.fcgi like that:\\r\\n\\r\\n{{{\\r\\ntrac@trac.example ~ $ ./trac-standalone-fcgi.py \\r\\n}}}\\r\\n\\r\\nThe above assumes that:\\r\\n * There is a user named ''trac'' for running trac instances and keeping trac environments in its home directory.\\r\\n * `/home/trac/instance` contains a trac environment\\r\\n * `/home/trac/htpasswd` contains authentication information\\r\\n * `/home/trac/run` is owned by the same group the nginx runs under\\r\\n  * and if your system is Linux the `/home/trac/run` has setgid bit set (`chmod g+s run`)\\r\\n  * and patch from ticket #T7239 is applied, or you''ll have to fix the socket file permissions every time\\r\\n\\r\\nUnfortunately nginx does not support variable expansion in fastcgi_pass directive. \\r\\nThus it is not possible to serve multiple trac instances from one server block. \\r\\n\\r\\nIf you worry enough about security, run trac instances under separate users. \\r\\n\\r\\nAnother way to run trac as a FCGI external application is offered in ticket #T6224\\r\\n\\r\\n----\\r\\nSee also:  TracGuide, TracInstall, [wiki:TracModWSGI ModWSGI], [wiki:TracCgi CGI], [wiki:TracModPython ModPython], [trac:TracNginxRecipe TracNginxRecipe]\\r\\n', NULL, NULL),\n('WikiMacros', 1, 1362994208291144, 'trac', '127.0.0.1', '= Trac Macros =\\r\\n\\r\\n[[PageOutline]]\\r\\n\\r\\nTrac macros are plugins to extend the Trac engine with custom ''functions'' written in Python. A macro inserts dynamic HTML data in any context supporting WikiFormatting. Its syntax is `[[macro-name(optional-arguments)]]`.\\r\\n\\r\\nThe WikiProcessors are another kind of macros. They typically deal with alternate markup formats and transformation of larger \"blocks\" of information (like source code highlighting). They are used for processing the multiline `{{{#!wiki-processor-name ... }}}` blocks.\\r\\n\\r\\n== Using Macros ==\\r\\n\\r\\nMacro calls are enclosed in two ''''square brackets''''. Like Python functions, macros can also have arguments, a comma separated list within parentheses.\\r\\n\\r\\n=== Getting Detailed Help ===\\r\\nThe list of available macros and the full help can be obtained using the !MacroList macro, as seen [#AvailableMacros below].\\r\\n\\r\\nA brief list can be obtained via `[[MacroList(*)]]` or `[[?]]`.\\r\\n\\r\\nDetailed help on a specific macro can be obtained by passing it as an argument to !MacroList, e.g. `[[MacroList(MacroList)]]`, or, more conveniently, by appending a question mark (`?`) to the macro''s name, like in `[[MacroList?]]`.\\r\\n\\r\\n\\r\\n\\r\\n=== Example ===\\r\\n\\r\\nA list of 3 most recently changed wiki pages starting with ''Trac'':\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  [[RecentChanges(Trac,3)]]\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em;\"\\r\\n[[RecentChanges(Trac,3)]]\\r\\n}}}\\r\\n|-----------------------------------\\r\\n{{{#!td\\r\\n  {{{\\r\\n  [[RecentChanges?(Trac,3)]]\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em;\"\\r\\n[[RecentChanges?(Trac,3)]]\\r\\n}}}\\r\\n|-----------------------------------\\r\\n{{{#!td\\r\\n  {{{\\r\\n  [[?]]\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em\"\\r\\n{{{#!html \\r\\n<div style=\"font-size: 80%\" class=\"trac-macrolist\">\\r\\n<h3><code>[[Image]]</code></h3>Embed an image in wiki-formatted text.\\r\\n\\r\\nThe first argument is the file …\\r\\n<h3><code>[[InterTrac]]</code></h3>Provide a list of known <a class=\"wiki\" href=\"/wiki/InterTrac\">InterTrac</a> prefixes.\\r\\n<h3><code>[[InterWiki]]</code></h3>Provide a description list for the known <a class=\"wiki\" href=\"/wiki/InterWiki\">InterWiki</a> prefixes.\\r\\n<h3><code>[[KnownMimeTypes]]</code></h3>List all known mime-types which can be used as <a class=\"wiki\" href=\"/wiki/WikiProcessors\">WikiProcessors</a>.\\r\\nCan be …</div>\\r\\n}}}\\r\\netc.\\r\\n}}}\\r\\n\\r\\n== Available Macros ==\\r\\n\\r\\n''''Note that the following list will only contain the macro documentation if you''ve not enabled `-OO` optimizations, or not set the `PythonOptimize` option for [wiki:TracModPython mod_python].''''\\r\\n\\r\\n[[MacroList]]\\r\\n\\r\\n== Macros from around the world ==\\r\\n\\r\\nThe [http://trac-hacks.org/ Trac Hacks] site provides a wide collection of macros and other Trac [TracPlugins plugins] contributed by the Trac community. If you''re looking for new macros, or have written one that you''d like to share with the world, please don''t hesitate to visit that site.\\r\\n\\r\\n== Developing Custom Macros ==\\r\\nMacros, like Trac itself, are written in the [http://python.org/ Python programming language] and are developed as part of TracPlugins.\\r\\n\\r\\nFor more information about developing macros, see the [trac:TracDev development resources] on the main project site.\\r\\n\\r\\n\\r\\nHere are 2 simple examples showing how to create a Macro with Trac 0.11. \\r\\n\\r\\nAlso, have a look at [trac:source:tags/trac-0.11/sample-plugins/Timestamp.py Timestamp.py] for an example that shows the difference between old style and new style macros and at the [trac:source:tags/trac-0.11/wiki-macros/README macros/README] which provides a little more insight about the transition.\\r\\n\\r\\n=== Macro without arguments ===\\r\\nTo test the following code, you should saved it in a `timestamp_sample.py` file located in the TracEnvironment''s `plugins/` directory.\\r\\n{{{\\r\\n#!python\\r\\nfrom datetime import datetime\\r\\n# Note: since Trac 0.11, datetime objects are used internally\\r\\n\\r\\nfrom genshi.builder import tag\\r\\n\\r\\nfrom trac.util.datefmt import format_datetime, utc\\r\\nfrom trac.wiki.macros import WikiMacroBase\\r\\n\\r\\nclass TimeStampMacro(WikiMacroBase):\\r\\n    \"\"\"Inserts the current time (in seconds) into the wiki page.\"\"\"\\r\\n\\r\\n    revision = \"$Rev$\"\\r\\n    url = \"$URL$\"\\r\\n\\r\\n    def expand_macro(self, formatter, name, text):\\r\\n        t = datetime.now(utc)\\r\\n        return tag.b(format_datetime(t, ''%c''))\\r\\n}}}\\r\\n\\r\\n=== Macro with arguments ===\\r\\nTo test the following code, you should saved it in a `helloworld_sample.py` file located in the TracEnvironment''s `plugins/` directory.\\r\\n{{{\\r\\n#!python\\r\\nfrom genshi.core import Markup\\r\\n\\r\\nfrom trac.wiki.macros import WikiMacroBase\\r\\n\\r\\nclass HelloWorldMacro(WikiMacroBase):\\r\\n    \"\"\"Simple HelloWorld macro.\\r\\n\\r\\n    Note that the name of the class is meaningful:\\r\\n     - it must end with \"Macro\"\\r\\n     - what comes before \"Macro\" ends up being the macro name\\r\\n\\r\\n    The documentation of the class (i.e. what you''re reading)\\r\\n    will become the documentation of the macro, as shown by\\r\\n    the !MacroList macro (usually used in the WikiMacros page).\\r\\n    \"\"\"\\r\\n\\r\\n    revision = \"$Rev$\"\\r\\n    url = \"$URL$\"\\r\\n\\r\\n    def expand_macro(self, formatter, name, text, args):\\r\\n        \"\"\"Return some output that will be displayed in the Wiki content.\\r\\n\\r\\n        `name` is the actual name of the macro (no surprise, here it''ll be\\r\\n        `''HelloWorld''`),\\r\\n        `text` is the text enclosed in parenthesis at the call of the macro.\\r\\n          Note that if there are ''''no'''' parenthesis (like in, e.g.\\r\\n          [[HelloWorld]]), then `text` is `None`.\\r\\n        `args` are the arguments passed when HelloWorld is called using a\\r\\n        `#!HelloWorld` code block.\\r\\n        \"\"\"\\r\\n        return ''Hello World, text = %s, args = %s'' % \\\\\\r\\n            (Markup.escape(text), Markup.escape(repr(args)))\\r\\n\\r\\n}}}\\r\\n\\r\\nNote that `expand_macro` optionally takes a 4^th^ parameter ''''`args`''''. When the macro is called as a [WikiProcessors WikiProcessor], it''s also possible to pass `key=value` [WikiProcessors#UsingProcessors processor parameters]. If given, those are stored in a dictionary and passed in this extra `args` parameter. On the contrary, when called as a macro, `args` is  `None`. (''''since 0.12'''').\\r\\n\\r\\nFor example, when writing:\\r\\n{{{\\r\\n{{{#!HelloWorld style=\"polite\" -silent verbose\\r\\n<Hello World!>\\r\\n}}}\\r\\n\\r\\n{{{#!HelloWorld\\r\\n<Hello World!>\\r\\n}}}\\r\\n\\r\\n[[HelloWorld(<Hello World!>)]]\\r\\n}}}\\r\\nOne should get:\\r\\n{{{\\r\\nHello World, text = <Hello World!> , args = {''style'': u''polite'', ''silent'': False, ''verbose'': True}\\r\\nHello World, text = <Hello World!> , args = {}\\r\\nHello World, text = <Hello World!> , args = None\\r\\n}}}\\r\\n\\r\\nNote that the return value of `expand_macro` is ''''''not'''''' HTML escaped. Depending on the expected result, you should escape it by yourself (using `return Markup.escape(result)`) or, if this is indeed HTML, wrap it in a Markup object (`return Markup(result)`) with `Markup` coming from Genshi, (`from genshi.core import Markup`).  \\r\\n\\r\\nYou can also recursively use a wiki Formatter (`from trac.wiki import Formatter`) to process the `text` as wiki markup, for example by doing:\\r\\n\\r\\n{{{\\r\\n#!python\\r\\nfrom genshi.core import Markup\\r\\nfrom trac.wiki.macros import WikiMacroBase\\r\\nfrom trac.wiki import Formatter\\r\\nimport StringIO\\r\\n\\r\\nclass HelloWorldMacro(WikiMacroBase):\\r\\n\tdef expand_macro(self, formatter, name, text, args):\\r\\n\t\ttext = \"whatever ''''''wiki'''''' markup you want, even containing other macros\"\\r\\n\t\t# Convert Wiki markup to HTML, new style\\r\\n\t\tout = StringIO.StringIO()\\r\\n\t\tFormatter(self.env, formatter.context).format(text, out)\\r\\n\t\treturn Markup(out.getvalue())\\r\\n}}}\\r\\n', NULL, NULL),\n('TracSearch', 1, 1362994208293019, 'trac', '127.0.0.1', '= Using Search =\\r\\n\\r\\nTrac has a built-in search engine to allow finding occurrences of keywords and substrings in wiki pages, tickets and changeset properties (author, revision and log message).\\r\\n\\r\\nUsing the Trac search facility is straightforward and its interface should be familiar to most users.\\r\\n\\r\\nApart from the [search: Search module], you will also find a small search field above the navigation bar at all time. It provides convenient access to the search module from all pages.\\r\\n\\r\\nThe search results show the most recent modifications ranked first in the results rather than the most relevant result.\\r\\n\\r\\n== \"Quickjump\" searches ==\\r\\nFor quick access to various project resources, the quick-search field at the top of every page can be used to enter a [TracLinks wiki link], which will take you directly to the resource identified by that link.\\r\\n\\r\\nFor example:\\r\\n\\r\\n * ![42] -- Opens change set 42\\r\\n * !#42 -- Opens ticket number 42\\r\\n * !{1} -- Opens report 1\\r\\n * /trunk -- Opens the browser for the `trunk` directory\\r\\n\\r\\n== Advanced ==\\r\\n\\r\\n=== Disabling Quickjumps ===\\r\\nTo disable the quickjump feature for a search keyword - for example when searching for occurences of the literal word !TracGuide - begin the query with an exclamation mark (`!`).\\r\\n\\r\\n=== Search Links ===\\r\\nFrom the Wiki, it is possible to link to a specific search, using\\r\\n`search:` links:\\r\\n * `search:?q=crash` will search for the string \"crash\" \\r\\n * `search:?q=trac+link&wiki=on` will search for \"trac\" and \"link\" \\r\\n   in wiki pages only\\r\\n\\r\\n----\\r\\nSee also: TracGuide, TracLinks, TracQuery', NULL, NULL);\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('WikiHtml', 1, 1362994208294592, 'trac', '127.0.0.1', '= Using HTML in Wiki Text =\\r\\n\\r\\nTrac supports inserting HTML into any wiki context, accomplished using the `#!html` [wiki:WikiProcessors WikiProcessor]. \\r\\n\\r\\nHowever a constraint is that this HTML has to be well-formed.\\r\\nIn particular you can''t insert a start tag in an `#!html` block,\\r\\nresume normal wiki text and insert the corresponding end tag in a \\r\\nsecond `#!html` block. \\r\\n\\r\\nFortunately, for creating styled <div>s, <span>s  or even complex tables\\r\\ncontaining arbitrary Wiki text, there''s a powerful alternative: use of\\r\\ndedicated `#!div`, `#!span` and `#!table`, `#!tr`, `#!td` and `#!th` blocks.\\r\\n\\r\\nThose Wiki processors are built-in, and does not require installing any additional packages.\\r\\n\\r\\n== How to use `#!html` == #HowtoUseHTML\\r\\nTo inform the wiki engine that a block of text should be treated as HTML, use the ''''html'''' processor. \\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  {{{\\r\\n  #!html\\r\\n  <h1 style=\"text-align: right; color: blue\">HTML Test</h1>\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em\"\\r\\n  {{{\\r\\n  #!html\\r\\n  <h1 style=\"text-align: right; color: blue\">HTML Test</h1>\\r\\n  }}}\\r\\n}}}\\r\\n\\r\\nNote that Trac sanitizes your HTML code before displaying it. That means that if you try to use potentially dangerous constructs such as Javascript event handlers, those will be removed from the output. \\r\\n\\r\\nSince 0.11, the filtering is done by Genshi, and as such, the produced output will be a well-formed fragment of HTML. As noted above in the introduction, this mean that you can no longer use two HTML blocks, one for opening a <div>, the second for closing it, in order to wrap arbitrary wiki text.\\r\\nThe new way to wrap any wiki content inside a <div> is to use the `#!div` Wiki  processor.\\r\\n\\r\\n== How to use `#!div` and `#!span` == #HowtoUseDivSpan\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  {{{\\r\\n  #!div class=\"important\" \\r\\n  **important** is a predefined class.\\r\\n  }}}\\r\\n  }}}\\r\\n  {{{\\r\\n  {{{\\r\\n  #!div style=\"border: 1pt dotted; margin: 1em\"\\r\\n  **wikipage** is another predefined class that will \\r\\n  be used when no class is specified.\\r\\n  }}}\\r\\n  }}}\\r\\n  {{{\\r\\n  {{{\\r\\n  #!div class=\"compact\" style=\"border: 1pt dotted; margin: 1em\"\\r\\n  **compact** is another predefined class reducing\\r\\n  the padding within the `<div>` to a minimum.\\r\\n  }}}\\r\\n  }}}\\r\\n  {{{\\r\\n  {{{\\r\\n  #!div class=\"wikipage compact\" style=\"border: 1pt dotted\"\\r\\n  Classes can be combined (here **wikipage** and **compact**)\\r\\n  which results in this case in reduced //vertical// \\r\\n  padding but there''s still some horizontal space for coping\\r\\n  with headings.\\r\\n  }}}\\r\\n  }}}\\r\\n  {{{\\r\\n  {{{\\r\\n  #!div class=\"\" style=\"border: 1pt dotted; margin: 1em\"\\r\\n  Explicitly specifying no classes is //not// the same\\r\\n  as specifying no class attribute, as this will remove\\r\\n  the //wikipage// default class.\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td style=\"padding-left: 2em\"\\r\\n\\r\\n  {{{\\r\\n  #!div class=\"important\" \\r\\n  **important** is a predefined class.\\r\\n  }}}\\r\\n\\r\\n  {{{\\r\\n  #!div style=\"border: 1pt dotted; margin: 1em\"\\r\\n  **wikipage** is another predefined class that will \\r\\n  be used when no class is specified.\\r\\n  }}}\\r\\n\\r\\n  {{{\\r\\n  #!div class=\"compact\" style=\"border: 1pt dotted; margin: 1em\"\\r\\n  **compact** is another predefined class reducing\\r\\n  the padding within the `<div>` to a minimum.\\r\\n  }}}\\r\\n\\r\\n  {{{\\r\\n  #!div class=\"wikipage compact\" style=\"border: 1pt dotted\"\\r\\n  Classes can be combined (here **wikipage** and **compact**)\\r\\n  which results in this case in reduced //vertical// \\r\\n  padding but there''s still some horizontal space for coping\\r\\n  with headings.\\r\\n  }}}\\r\\n\\r\\n  {{{\\r\\n  #!div class=\"\" style=\"border: 1pt dotted; margin: 1em\"\\r\\n  Explicitly specifying no classes is //not// the same\\r\\n  as specifying no class attribute, as this will remove\\r\\n  the //wikipage// default class.\\r\\n  }}}\\r\\n\\r\\n}}}\\r\\n\\r\\nNote that the contents of a `#!div` block are contained in one or more paragraphs, which have a non-zero top and bottom margin. This leads to the top and bottom padding in the example above. To remove the top and bottom margin of the contents, add the `compact` class to the `#!div`. Another predefined class besides `wikipage` and `compact` is `important`, which can be used to make a paragraph stand out. Extra CSS classes can be defined via the `site/style.css` file for example, see TracInterfaceCustomization#SiteAppearance.\\r\\n\\r\\nFor spans, you should rather use the Macro call syntax:\\r\\n||= Wiki Markup =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  Hello \\r\\n  [[span(''''WORLD'''' (click [#anchor here]), style=color: green; font-size: 120%, id=anchor)]]!\\r\\n  }}}\\r\\n}}}\\r\\n|---------------------------------------------------------------------------------\\r\\n||= Display =||\\r\\n{{{#!td style=\"padding-left: 2em\"\\r\\n  Hello\\r\\n  [[span(''''WORLD'''' (click [#anchor here]), style=color: green; font-size: 120%, id=anchor)]]!\\r\\n}}}\\r\\n\\r\\n== How to use `#!td` and other table related processors == #Tables\\r\\n\\r\\n`#!td` or `#!th` processors are actually the main ones, for creating table data and header cells, respectively. The other processors `#!table` and `#!tr` are not required for introducing a table structure, as `#!td` and `#!th` will do this automatically. The `|-` row separator can be used to start a new row when needed, but some may prefer to use a `#!tr` block for that, as this introduces a more formal grouping and offers the possibility to use an extra level of indentation. The main purpose of the `#!table` and `#!tr` is to give the possibility to specify HTML attributes, like ''''style'''' or ''''valign'''' to these elements.\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n {{{\\r\\n Simple 2x2 table with rich content:\\r\\n {{{#!th align=left\\r\\n  - Left\\r\\n  - Header\\r\\n }}}\\r\\n {{{#!th align=left\\r\\n  - Right\\r\\n  - Header\\r\\n }}}\\r\\n |----------------------------------\\r\\n {{{#!td style=\"background: #ffd\"\\r\\n  - Left\\r\\n  - Content\\r\\n }}}\\r\\n {{{#!td style=\"vertical-align: top\"\\r\\n !RightContent\\r\\n }}}\\r\\n |----------------------------------\\r\\n || ... and this can be mixed||\\\\\\r\\n ||with pipe-based cells ||\\r\\n {{{#!td colspan=2\\r\\n Pick the style the more appropriate\\r\\n to your content\\r\\n \\r\\n See WikiFormatting#Tables for details\\r\\n on the pipe-based table syntax.\\r\\n }}}\\r\\n \\r\\n If one needs to add some \\r\\n attributes to the table itself...\\r\\n \\r\\n {{{\\r\\n #!table style=\"border:none;text-align:center;margin:auto\"\\r\\n   {{{#!tr ====================================\\r\\n     {{{#!th style=\"border: none\"\\r\\n     Left header\\r\\n     }}}\\r\\n     {{{#!th style=\"border: none\"\\r\\n     Right header\\r\\n     }}}\\r\\n   }}}\\r\\n   {{{#!tr ==== style=\"border: 1px dotted grey\"\\r\\n     {{{#!td style=\"border: none\"\\r\\n     1.1\\r\\n     }}}\\r\\n     {{{#!td style=\"border: none\"\\r\\n     1.2\\r\\n     }}}\\r\\n   }}}\\r\\n   {{{#!tr ====================================\\r\\n     {{{#!td style=\"border: none\"\\r\\n     2.1\\r\\n     }}}\\r\\n     {{{#!td\\r\\n     2.2\\r\\n     }}}\\r\\n   }}}\\r\\n }}}\\r\\n\\r\\n\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td valign=top\\r\\nSimple 2x2 table with rich content:\\r\\n{{{#!th align=left\\r\\n - Left\\r\\n - Header\\r\\n}}}\\r\\n{{{#!th align=left\\r\\n - Right\\r\\n - Header\\r\\n}}}\\r\\n|----------------------------------\\r\\n{{{#!td style=\"background: #ffd\"\\r\\n - Left\\r\\n - Content\\r\\n}}}\\r\\n{{{#!td style=\"vertical-align: top\"\\r\\n!RightContent\\r\\n}}}\\r\\n|----------------------------------\\r\\n|| ... and this can be mixed||\\\\\\r\\n||with pipe-based cells ||\\r\\n{{{#!td colspan=2\\r\\nPick the style the more appropriate\\r\\nto your content\\r\\n\\r\\nSee WikiFormatting#Tables for details\\r\\non the pipe-based table syntax.\\r\\n}}}\\r\\n\\r\\nIf one needs to add some \\r\\nattributes to the table itself...\\r\\n\\r\\n{{{\\r\\n#!table style=\"border:none;text-align:center;margin:auto\"\\r\\n  {{{#!tr ====================================\\r\\n    {{{#!th style=\"border: none\"\\r\\n    Left header\\r\\n    }}}\\r\\n    {{{#!th style=\"border: none\"\\r\\n    Right header\\r\\n    }}}\\r\\n  }}}\\r\\n  {{{#!tr ==== style=\"border: 1px dotted grey\"\\r\\n    {{{#!td style=\"border: none\"\\r\\n    1.1\\r\\n    }}}\\r\\n    {{{#!td style=\"border: none\"\\r\\n    1.2\\r\\n    }}}\\r\\n  }}}\\r\\n  {{{#!tr ====================================\\r\\n    {{{#!td style=\"border: none\"\\r\\n    2.1\\r\\n    }}}\\r\\n    {{{#!td\\r\\n    2.2\\r\\n    }}}\\r\\n  }}}\\r\\n}}}\\r\\n}}}\\r\\n\\r\\nNote that by default tables are assigned the \"wiki\" CSS class, which gives a distinctive look to the header cells and a default border to the table and cells (as can be seen for the tables on this page). By removing this class (`#!table class=\"\"`), one regains complete control on the table presentation. In particular, neither the table, the rows nor the cells will have a border, so this is a more effective way to get such an effect than having to specify a `style=\"border: no\"` parameter everywhere. \\r\\n\\r\\n{{{#!table class=\"\"\\r\\n||= Wiki Markup =||= Display =||\\r\\n {{{#!td\\r\\n  {{{\\r\\n  {{{#!table class=\"\"\\r\\n  ||  0||  1||  2||\\r\\n  || 10|| 20|| 30||\\r\\n  || 11|| 22|| 33||\\r\\n  ||||||=  numbers  =||\\r\\n  }}}\\r\\n  }}}\\r\\n }}}\\r\\n {{{#!td\\r\\n  {{{#!table class=\"\"\\r\\n  ||  0||  1||  2||\\r\\n  || 10|| 20|| 30||\\r\\n  || 11|| 22|| 33||\\r\\n  ||||||=  numbers  =||\\r\\n  }}}\\r\\n }}}\\r\\n}}}\\r\\n\\r\\nOther classes can be specified as alternatives (remember that you can define your own in [TracInterfaceCustomization#SiteAppearance site/style.css]).\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  {{{#!table class=\"listing\"\\r\\n  ||  0||  1||  2||\\r\\n  || 10|| 20|| 30||\\r\\n  || 11|| 22|| 33||\\r\\n  ||||||=  numbers  =||\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n  {{{#!table class=\"listing\"\\r\\n  ||  0||  1||  2||\\r\\n  || 10|| 20|| 30||\\r\\n  || 11|| 22|| 33||\\r\\n  ||||||=  numbers  =||\\r\\n  }}}\\r\\n}}}\\r\\n\\r\\n\\r\\n== HTML comments ==\\r\\nHTML comments are stripped from the output of the `html` processor. To add an HTML comment to a wiki page, use the `htmlcomment` processor (available since 0.12). For example, the following code block:\\r\\n||= Wiki Markup =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  {{{\\r\\n  #!htmlcomment\\r\\n  This block is translated to an HTML comment.\\r\\n  It can contain <tags> and &entities; that will not be escaped in the output.\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n|---------------------------------------------------------------------------------\\r\\n||= Display =||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  <!--\\r\\n  This block is translated to an HTML comment.\\r\\n  It can contain <tags> and &entities; that will not be escaped in the output.\\r\\n  -->\\r\\n  }}}\\r\\n}}}\\r\\n\\r\\nPlease note that the character sequence \"`--`\" is not allowed in HTML comments, and will generate a rendering error.\\r\\n\\r\\n\\r\\n== More Information ==\\r\\n\\r\\n * http://www.w3.org/ -- World Wide Web Consortium\\r\\n * http://www.w3.org/MarkUp/ -- HTML Markup Home Page\\r\\n\\r\\n----\\r\\nSee also:  WikiProcessors, WikiFormatting, WikiRestructuredText\\r\\n', NULL, NULL),\n('TracImport', 1, 1362994208296441, 'trac', '127.0.0.1', '= Importing ticket data =\\r\\n[[PageOutline]]\\r\\n\\r\\nBy means of migrating from other issue-tracking systems, perform some external actions over tickets or simply synchronize different data bases, there are some available tools, plug-ins or scripts which lets you import or up-date tickets into Trac.\\r\\n\\r\\nBelow, follows a collection of some of those.\\r\\n\\r\\n== !TicketImportPlugin ==\\r\\n\\r\\n [http://trac-hacks.org/wiki/TicketImportPlugin TicketImportPlugin] :: mainly, but not only, this plug-in lets you import or up-date into Trac a series of tickets from a ''''''CSV file'''''' or (if the [http://pypi.python.org/pypi/xlrd xlrd library] is installed) from an ''''''Excel file''''''. \\r\\n\\r\\n== !ExportImportXlsPlugin ==\\r\\n\\r\\n [http://trac-hacks.org/wiki/ExportImportXlsPlugin ExportImportXlsPlugin] :: this plug-in add an admin panel for export and import tickets via ''''''XLS file''''''.\\r\\n  * It depends on the python packages xlwt/rxld.\\r\\n\\r\\n== Bugzilla ==\\r\\n\\r\\n [http://trac-hacks.org/wiki/BugzillaIssueTrackingPlugin BugzillaIssueTrackingPlugin] :: integrates Bugzilla into Trac keeping TracLinks\\r\\n\\r\\nTicket data can be imported from Bugzilla using the [http://trac.edgewall.org/browser/trunk/contrib/bugzilla2trac.py bugzilla2trac.py] script, available in the contrib/ directory of the Trac distribution.\\r\\n\\r\\n{{{\\r\\n$ bugzilla2trac.py\\r\\nbugzilla2trac - Imports a bug database from Bugzilla into Trac.\\r\\n\\r\\nUsage: bugzilla2trac.py [options]\\r\\n\\r\\nAvailable Options:\\r\\n  --db <MySQL dbname>              - Bugzilla''s database\\r\\n  --tracenv /path/to/trac/env      - full path to Trac db environment\\r\\n  -h | --host <MySQL hostname>     - Bugzilla''s DNS host name\\r\\n  -u | --user <MySQL username>     - effective Bugzilla''s database user\\r\\n  -p | --passwd <MySQL password>   - Bugzilla''s user password\\r\\n  -c | --clean                     - remove current Trac tickets before importing\\r\\n  --help | help                    - this help info\\r\\n\\r\\nAdditional configuration options can be defined directly in the script.\\r\\n}}}\\r\\n\\r\\nCurrently, the following data is imported from Bugzilla:\\r\\n\\r\\n  * bugs\\r\\n  * bug activity (field changes)\\r\\n  * bug attachments\\r\\n  * user names and passwords (put into a htpasswd file)\\r\\n\\r\\nThe script provides a number of features to ease the conversion, such as:\\r\\n\\r\\n  * PRODUCT_KEYWORDS:  Trac doesn''t have the concept of products, so the script provides the ability to attach a ticket keyword instead.\\r\\n\\r\\n  * IGNORE_COMMENTS:  Don''t import Bugzilla comments that match a certain regexp.\\r\\n\\r\\n  * STATUS_KEYWORDS:  Attach ticket keywords for the Bugzilla statuses not available in Trac.  By default, the ''VERIFIED'' and ''RELEASED'' Bugzilla statuses are translated into Trac keywords.\\r\\n\\r\\nFor more details on the available options, see the configuration section at the top of the script.\\r\\n\\r\\n== Jira ==\\r\\n\\r\\n [http://trac-hacks.org/wiki/JiraToTracIntegration JiraToTracIntegration] :: provides tools to import Atlassian Jira backup files into Trac. The plug-in consists of a Python 3.1 commandline tool that:\\r\\n   - Parses the Jira backup XML file\\r\\n   - Sends the imported Jira data and attachments to Trac using the [http://trac-hacks.org/wiki/XmlRpcPlugin XmlRpcPlugin]\\r\\n   - Generates a htpasswd file containing the imported Jira users and their SHA-512 base64 encoded passwords\\r\\n\\r\\n== Mantis ==\\r\\n\\r\\n [http://trac-hacks.org/wiki/MantisImportScript MantisImportScript] :: script to import from Mantis into Trac the following data:\\r\\n  * bugs\\r\\n  * bug comments\\r\\n  * bug activity (field changes)\\r\\n  * attachments (as long as the files live in the mantis db, not on the filesystem) .\\r\\n\\r\\n== !PlanetForge ==\\r\\n\\r\\n [http://trac-hacks.org/wiki/PlanetForgeImportExportPlugin PlanetForgeImportExportPlugin] :: this plugin exports Trac data (wiki, tickets, compoments, permissions, repositories, etc.) using the open format designed by the COCLICO project. It extends the webadmin panel and the ''trac admin ...'' command. Still has no ''import'' feature. \\r\\n\\r\\n== Scarab ==\\r\\n\\r\\n [http://trac-hacks.org/wiki/ScarabToTracScript ScarabToTracScript] :: script that migrates Scarab issues to Trac tickets\\r\\n    * Requires [http://trac-hacks.org/wiki/XmlRpcPlugin XmlRpcPlugin]\\r\\n\\r\\n== Sourceforge ==\\r\\n\\r\\n [http://trac-hacks.org/wiki/SfnToTracScript SfnToTracScript] :: importer of !SourceForge''s new backup file (originated from #Trac3521)\\r\\n\\r\\nAlso, ticket data can be imported from Sourceforge using the [http://trac.edgewall.org/browser/trunk/contrib/sourceforge2trac.py sourceforge2trac.py] script, available in the contrib/ directory of the Trac distribution.\\r\\n\\r\\n== Other ==\\r\\n\\r\\nSince trac uses a SQL database to store the data, you can import from other systems by examining the database tables. Just go into [http://www.sqlite.org/sqlite.html sqlite] command line to look at the tables and import into them from your application.\\r\\n\\r\\n=== Comma delimited file - CSV ===\\r\\nSee [http://trac.edgewall.org/attachment/wiki/TracSynchronize/csv2trac.2.py csv2trac.2.py] for details.  This approach is particularly useful if one needs to enter a large number of tickets by hand. (note that the ticket type type field, (task etc...) is also needed for this script to work with more recent Trac releases)\\r\\nComments on script: The script has an error on line 168, (''Ticket'' needs to be ''ticket'').  Also, the listed values for severity and priority are swapped. \\r\\n\\r\\n----\\r\\nSee also: \\r\\n * to import/export wiki pages: TracAdmin, \\r\\n * to export tickets: TracTickets, TracQuery', NULL, NULL),\n('TracLinks', 1, 1362994208298042, 'trac', '127.0.0.1', '= Trac Links =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nTracLinks are a fundamental feature of Trac, because they allow easy hyperlinking between the various entities in the system—such as tickets, reports, changesets, Wiki pages, milestones, and source files—from anywhere WikiFormatting is used.\\r\\n\\r\\nTracLinks are generally of the form ''''''type:id'''''' (where ''''id'''' represents the\\r\\nnumber, name or path of the item) though some frequently used kinds of items\\r\\nalso have short-hand notations.\\r\\n\\r\\n== Where to use TracLinks ==\\r\\nYou can use TracLinks in:\\r\\n\\r\\n * Source code (Subversion) commit messages\\r\\n * Wiki pages\\r\\n * Full descriptions for tickets, reports and milestones\\r\\n\\r\\nand any other text fields explicitly marked as supporting WikiFormatting.\\r\\n\\r\\n== Overview ==\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td\\r\\n Wiki pages :: `CamelCase` or `wiki:CamelCase`\\r\\n Parent page :: `[..]`\\r\\n Tickets :: `#1` or `ticket:1`\\r\\n Ticket comments :: `comment:1:ticket:2`\\r\\n Reports :: `{1}` or `report:1`\\r\\n Milestones :: `milestone:1.0`\\r\\n Attachment :: `attachment:example.tgz` (for current page attachment), `attachment:attachment.1073.diff:ticket:944` (absolute path)\\r\\n Changesets :: `r1`, `[1]`, `changeset:1` or (restricted) `[1/trunk]`, `changeset:1/trunk`\\r\\n Revision log :: `r1:3`, `[1:3]` or `log:@1:3`, `log:trunk@1:3`, `[2:5/trunk]`\\r\\n Diffs :: `diff:@1:3`, `diff:plugins/0.12/mercurial-plugin@9128:9953`,\\r\\n          `diff:tags/trac-0.9.2/wiki-default//tags/trac-0.9.3/wiki-default` \\r\\n          or `diff:trunk/trac@3538//sandbox/vc-refactoring@3539`\\r\\n Files :: `source:trunk/COPYING`, `source:/trunk/COPYING@200` (at version 200), `source:/trunk/COPYING@200#L25` (at version 200, line 25)\\r\\n}}}\\r\\n{{{#!td\\r\\n Wiki pages :: CamelCase or wiki:CamelCase\\r\\n Parent page :: [..]\\r\\n Tickets :: #1 or ticket:1\\r\\n Ticket comments :: comment:1:ticket:2 \\r\\n Reports :: {1} or report:1\\r\\n Milestones :: milestone:1.0\\r\\n Attachment :: attachment:example.tgz (for current page attachment), attachment:attachment.1073.diff:ticket:944 (absolute path)\\r\\n Changesets :: r1, [1], changeset:1 or (restricted) [1/trunk], changeset:1/trunk\\r\\n Revision log :: r1:3, [1:3] or log:@1:3, log:trunk@1:3, [2:5/trunk]\\r\\n Diffs :: diff:@1:3, diff:plugins/0.12/mercurial-plugin@9128:9953,\\r\\n          diff:tags/trac-0.9.2/wiki-default//tags/trac-0.9.3/wiki-default \\r\\n          or diff:trunk/trac@3538//sandbox/vc-refactoring@3539\\r\\n Files :: source:trunk/COPYING, source:/trunk/COPYING@200 (at version 200), source:/trunk/COPYING@200#L25 (at version 200, line 25)\\r\\n}}}\\r\\n\\r\\n''''''Note:'''''' The wiki:CamelCase form is rarely used, but it can be convenient to refer to\\r\\npages whose names do not follow WikiPageNames rules, i.e., single words,\\r\\nnon-alphabetic characters, etc. See WikiPageNames for more about features specific\\r\\nto links to Wiki page names.\\r\\n\\r\\n\\r\\n{{{#!table class=\"\"\\r\\n|||| Trac links using the full (non-shorthand) notation can also be given a custom link title like this: ||\\r\\n{{{#!td\\r\\n{{{\\r\\n[ticket:1 This is a link to ticket number one] or\\r\\n[[ticket:1|This is another link to ticket number one]].\\r\\n}}}\\r\\n}}}\\r\\n{{{#!td\\r\\n[ticket:1 This is a link to ticket number one] or\\r\\n[[ticket:1|This is another link to ticket number one]].\\r\\n}}}\\r\\n|--------------------------------------------------------------------------------------\\r\\n|||| If the title is omitted, only the id (the part after the colon) is displayed:  ||\\r\\n{{{#!td\\r\\n{{{\\r\\n[ticket:1] or [[ticket:2]]\\r\\n}}}\\r\\n}}}\\r\\n{{{#!td\\r\\n[ticket:1] or [[ticket:2]]\\r\\n}}}\\r\\n|--------------------------------------------------------------------------------------\\r\\n|||| `wiki` is the default if the namespace part of a full link is omitted:  || \\r\\n{{{#!td\\r\\n{{{\\r\\n[SandBox the sandbox] or\\r\\n[[SandBox|the sandbox]]\\r\\n}}}\\r\\n}}}\\r\\n{{{#!td\\r\\n[SandBox the sandbox] or\\r\\n[[SandBox|the sandbox]]\\r\\n}}}\\r\\n|--------------------------------------------------------------------------------------\\r\\n|||| The short form ''''realm:target'''' can also be wrapped within a <...> pair, [[br]] which allow for arbitrary characters (i.e. anything but >)  ||\\r\\n{{{#!td\\r\\n{{{\\r\\n<wiki:Strange(page@!)>\\r\\n}}}\\r\\n}}}\\r\\n{{{#!td\\r\\n<wiki:Strange(page@!)>\\r\\n}}}\\r\\n}}}\\r\\n\\r\\nTracLinks are a very simple idea, but actually allow quite a complex network of information. In practice, it''s very intuitive and simple to use, and we''ve found the \"link trail\" extremely helpful to better understand what''s happening in a project or why a particular change was made.\\r\\n\\r\\n\\r\\n== Advanced use of TracLinks ==\\r\\n\\r\\n=== Relative links ===\\r\\n\\r\\nTo create a link to a [trac:SubWiki SubWiki]-page just use a ''/'':\\r\\n{{{\\r\\n WikiPage/SubWikiPage or ./SubWikiPage\\r\\n}}}\\r\\n\\r\\nTo link from a [trac:SubWiki SubWiki] page to a parent, simply use a ''..'':\\r\\n{{{\\r\\n  [..] or [[..]]\\r\\n}}}\\r\\n  [..] or [[..]]\\r\\n\\r\\nTo link from a [trac:SubWiki SubWiki] page to a [=#sibling sibling] page, use a ''../'':\\r\\n{{{\\r\\n  [../Sibling see next sibling] or [[../Sibling|see next sibling]]\\r\\n}}}\\r\\n  [../Sibling see next sibling] or [[../Sibling|see next sibling]]\\r\\n\\r\\nBut in practice you often won''t need to add the `../` prefix to link to a sibling page.\\r\\nFor resolving the location of a wiki link, it''s the target page closest in the hierarchy\\r\\nto the page where the link is written which will be selected. So for example, within \\r\\na sub-hierarchy, a sibling page will be targeted in preference to a toplevel page.\\r\\nThis makes it easy to copy or move pages to a sub-hierarchy by [[WikiNewPage#renaming|renaming]] without having to adapt the links.\\r\\n\\r\\nIn order to link explicitly to a [=#toplevel toplevel] Wiki page,\\r\\nuse the `wiki:/` prefix.\\r\\nBe careful **not** to use the `/` prefix alone, as this corresponds to the\\r\\n[#Server-relativelinks] syntax and with such a link you will lack the `/wiki/` \\r\\npart in the resulting URL.\\r\\n\\r\\n''''(Changed in 0.11)'''' Note that in Trac 0.10, using e.g. `[../newticket]`  may have worked for linking to the `/newticket` top-level URL, but since 0.11, such a link will stay in the wiki namespace and therefore link to a sibling page. \\r\\nSee [#Server-relativelinks] for the new syntax.\\r\\n\\r\\n=== Link anchors ===\\r\\n\\r\\nTo create a link to a specific anchor in a page, use ''#'':\\r\\n{{{\\r\\n [#Linkanchors Link anchors] or [[#Linkanchors|Link anchors]]\\r\\n}}}\\r\\n  [#Linkanchors Link anchors] or [[#Linkanchors|Link anchors]]\\r\\n\\r\\nHint: when you move your mouse over the title of a section, a ''¶'' character will be displayed. This is a link to that specific section and you can use this to copy the `#...` part inside a relative link to an anchor.\\r\\n\\r\\nTo create a link to the first or last occurrence of a term on a page, use a ''''pseudo anchor'''' starting with ''#/'' or ''#?'':\\r\\n{{{\\r\\n [#/Milestone first occurrence of Milestone] or\\r\\n [#?Milestone last occurrence of Milestone]\\r\\n}}}\\r\\n [#/Milestone first occurrence of Milestone] or\\r\\n [#?Milestone last occurrence of Milestone]\\r\\nThis will also highlight all other matches on the linked page. By default only case sensitive matches are considered. To include case insensitive matches append ''/i'':\\r\\n{{{\\r\\n [#/Milestone/i first occurrence of Milestone or milestone] or\\r\\n [#?Milestone/i last occurrence of Milestone or milestone]\\r\\n}}}\\r\\n [#/Milestone/i first occurrence of Milestone or milestone] or\\r\\n [#?Milestone/i last occurrence of Milestone or milestone]\\r\\n\\r\\n''''(since Trac 1.0)''''\\r\\n\\r\\nSuch anchors can be very useful for linking to specific lines in a file in the source browser:\\r\\n{{{\\r\\n [trac:source:tags/trac-0.12/trac/wiki/api.py#L127 Line 127] or\\r\\n [trac:source:tags/trac-0.12/trac/ticket/roadmap.py#L47 Line 47]\\r\\n}}}\\r\\n [trac:source:tags/trac-0.12/trac/wiki/api.py#L127 Line 127] or\\r\\n [trac:source:tags/trac-0.12/trac/ticket/roadmap.py#L47 Line 47]\\r\\n(Hint: The line numbers displayed in the source browser are links to anchors on the respective lines.)\\r\\n\\r\\nSince such links become outdated when the file changes, it can be useful to link using a ''#/'' pseudo anchor instead:\\r\\n{{{\\r\\n [trac:source:trunk/trac/wiki/api.py#/IWikiSyntaxProvider IWikiSyntaxProvider] or\\r\\n [trac:source:trunk/trac/env.py#/ISystemInfoProvider ISystemInfoProvider]\\r\\n}}}\\r\\n [trac:source:trunk/trac/wiki/api.py#/IWikiSyntaxProvider IWikiSyntaxProvider] or\\r\\n [trac:source:trunk/trac/env.py#/ISystemInfoProvider ISystemInfoProvider]\\r\\n\\r\\n=== InterWiki links ===\\r\\n\\r\\nOther prefixes can be defined freely and made to point to resources in other Web applications. The definition of those prefixes as well as the URLs of the corresponding Web applications is defined in a special Wiki page, the InterMapTxt page. Note that while this could be used to create links to other Trac environments, there''s a more specialized way to register other Trac environments which offers greater flexibility.\\r\\n\\r\\n=== InterTrac links ===\\r\\n\\r\\nThis can be seen as a kind of InterWiki link specialized for targeting other Trac projects.\\r\\n\\r\\nAny type of Trac link can be written in one Trac environment and actually refer to resources in another Trac environment. All that is required is to prefix the Trac link with the name of the other Trac environment followed by a colon. The other Trac environment must be registered on the InterTrac page. \\r\\n\\r\\nA distinctive advantage of InterTrac links over InterWiki links is that the shorthand form of Trac links (e.g. `{}`, `r`, `#`) can also be used. For example if T was set as an alias for Trac, links to Trac tickets can be written #T234, links to Trac changesets can be written [trac 1508].\\r\\nSee InterTrac for the complete details. \\r\\n\\r\\n=== Server-relative links ===\\r\\n\\r\\nIt is often useful to be able to link to objects in your project that\\r\\nhave no built-in Trac linking mechanism, such as static resources, `newticket`,\\r\\na shared `/register` page on the server, etc.\\r\\n\\r\\nTo link to resources inside the project, use either an absolute path from the project root, \\r\\nor a relative link from the URL of the current page (''''Changed in 0.11''''):\\r\\n\\r\\n{{{\\r\\n[/newticket Create a new ticket] or [[//newticket|Create a new ticket]]\\r\\n[/ home] or [[/|home]]\\r\\n}}}\\r\\n\\r\\nDisplay: [/newticket Create a new ticket] or [[//newticket|Create a new ticket]]\\r\\n[/ home] or [[/|home]]\\r\\n\\r\\nTo link to another location on the server (possibly outside the project but on the same host), use the `//` prefix (''''Changed in 0.11''''):\\r\\n\\r\\n{{{\\r\\n[//register Register Here] or [[//register|Register Here]]\\r\\n}}}\\r\\n\\r\\nDisplay: [//register Register Here] or [[//register|Register Here]]\\r\\n\\r\\n=== Quoting space in TracLinks ===\\r\\n\\r\\nImmediately after a TracLinks prefix, targets containing space characters should\\r\\nbe enclosed in a pair of quotes or double quotes.\\r\\nExamples:\\r\\n * !wiki:\"The whitespace convention\"\\r\\n * !attachment:''the file.txt'' or\\r\\n * !attachment:\"the file.txt\" \\r\\n * !attachment:\"the file.txt:ticket:123\" \\r\\n\\r\\nNote that by using [trac:WikiCreole] style links, it''s quite natural to write links containing spaces:\\r\\n * ![[The whitespace convention]]\\r\\n * ![[attachment:the file.txt]]\\r\\n\\r\\n=== Escaping Links ===\\r\\n\\r\\nTo prevent parsing of a !TracLink, you can escape it by preceding it with a ''!'' (exclamation mark).\\r\\n{{{\\r\\n !NoLinkHere.\\r\\n ![42] is not a link either.\\r\\n}}}\\r\\n\\r\\nDisplay:\\r\\n !NoLinkHere.\\r\\n ![42] is not a link either.\\r\\n\\r\\n\\r\\n=== Parameterized Trac links ===\\r\\n\\r\\nMany Trac resources have more than one way to be rendered, depending on some extra parameters. For example, a Wiki page can accept a `version` or a `format` parameter, a report can make use of dynamic variables, etc.\\r\\n\\r\\nTrac links can support an arbitrary set of parameters, written in the same way as they would be for the corresponding URL. Some examples:\\r\\n - `wiki:WikiStart?format=txt`\\r\\n - `ticket:1?version=1`\\r\\n - `[/newticket?component=module1 create a ticket for module1]`\\r\\n - `[/newticket?summary=Add+short+description+here create a ticket with URL with spaces]`\\r\\n\\r\\n\\r\\n== TracLinks Reference ==\\r\\nThe following sections describe the individual link types in detail, as well as notes on advanced usage of links.\\r\\n\\r\\n=== attachment: links ===\\r\\n\\r\\nThe link syntax for attachments is as follows:\\r\\n * !attachment:the_file.txt creates a link to the attachment the_file.txt of the current object\\r\\n * !attachment:the_file.txt:wiki:MyPage creates a link to the attachment the_file.txt of the !MyPage wiki page\\r\\n * !attachment:the_file.txt:ticket:753 creates a link to the attachment the_file.txt of the ticket 753\\r\\n\\r\\nNote that the older way, putting the filename at the end, is still supported: !attachment:ticket:753:the_file.txt.\\r\\n\\r\\nIf you''d like to create a direct link to the content of the attached file instead of a link to the attachment page, simply use `raw-attachment:` instead of `attachment:`.\\r\\n\\r\\nThis can be useful for pointing directly to an HTML document, for example. Note that for this use case, you''d have to allow the web browser to render the content by setting `[attachment] render_unsafe_content = yes` (see TracIni#attachment-section). Caveat: only do that in environments for which you''re 100% confident you can trust the people who are able to attach files, as otherwise this would open up your site to [wikipedia:Cross-site_scripting cross-site scripting] attacks.\\r\\n\\r\\nSee also [#export:links].\\r\\n\\r\\n=== comment: links ===\\r\\n\\r\\nWhen you''re inside a given ticket, you can simply write e.g. !comment:3 to link to the third change comment.\\r\\nIt is possible to link to a comment of a specific ticket from anywhere using one of the following syntax:\\r\\n - `comment:3:ticket:123` \\r\\n - `ticket:123#comment:3` (note that you can''t write `#123#!comment:3`!)\\r\\nIt is also possible to link to the ticket''s description using one of the following syntax:\\r\\n - `comment:description` (within the ticket)\\r\\n - `comment:description:ticket:123`\\r\\n - `ticket:123#comment:description`\\r\\n\\r\\n=== htdocs: links ===\\r\\n\\r\\nUse `htdocs:path/to/file` to reference files in the `htdocs` directory of the Trac environment, the [TracEnvironment#DirectoryStructure web resource directory].\\r\\n\\r\\n=== query: links ===\\r\\n\\r\\nSee TracQuery#UsingTracLinks and [#ticket:links].\\r\\n\\r\\n=== search: links ===\\r\\n\\r\\nSee TracSearch#SearchLinks \\r\\n\\r\\n=== ticket: links ===\\r\\n ''''alias:'''' `bug:`\\r\\n\\r\\nBesides the obvious `ticket:id` form, it is also possible to specify a list of tickets or even a range of tickets instead of the `id`. This generates a link to a custom query view containing this fixed set of tickets.\\r\\n\\r\\nExample: \\r\\n - `ticket:5000-6000`\\r\\n - `ticket:1,150`\\r\\n\\r\\n''''(since Trac 0.11)''''\\r\\n\\r\\n=== timeline: links ===\\r\\n\\r\\nLinks to the timeline can be created by specifying a date in the ISO:8601 format. The date can be optionally followed by a time specification. The time is interpreted as being UTC time, but alternatively you can specify your local time, followed by your timezone if you don''t want to compute the UTC time.\\r\\n\\r\\nExamples:\\r\\n - `timeline:2008-01-29`\\r\\n - `timeline:2008-01-29T15:48`\\r\\n - `timeline:2008-01-29T15:48Z`\\r\\n - `timeline:2008-01-29T16:48+01`\\r\\n\\r\\n''''(since Trac 0.11)''''\\r\\n\\r\\n=== wiki: links ===\\r\\n\\r\\nSee WikiPageNames and [#QuotingspaceinTracLinks quoting space in TracLinks] above. It is possible to create a link to a specific page revision using the syntax WikiStart@1.\\r\\n\\r\\n=== Version Control related links ===\\r\\n\\r\\nIt should be noted that multiple repository support works by creating a kind of virtual namespace for versioned files in which the toplevel folders correspond to the repository names. Therefore, in presence of multiple repositories, a ''''/path'''' specification in the syntax of links detailed below should start with the name of the repository. If omitted, the default repository is used. In case a toplevel folder of the default repository has the same name as a repository, the latter \"wins\". One can always access such folder by fully qualifying it (the default repository can be an alias of a named repository, or conversely, it is always possible to create an alias for the default repository, ask your Trac administrator).\\r\\n\\r\\nFor example, `source:/trunk/COPYING` targets the path `/trunk/COPYING` in the default repository, whereas `source:/projectA/trunk/COPYING` targets the path `/trunk/COPYING` in the repository named `projectA`. This can be the same file if `''projectA''` is an alias to the default repository or if `''''` (the default repository) is an alias to `''projectA''`.\\r\\n\\r\\n==== source: links ====\\r\\n ''''aliases:'''' `browser:`, `repos:`\\r\\n\\r\\nThe default behavior for a source:/some/path link is to open the browser in that directory directory \\r\\nif the path points to a directory or to show the latest content of the file.\\r\\n\\r\\nIt''s also possible to link directly to a specific revision of a file like this:\\r\\n - `source:/some/file@123` - link to the file''s revision 123\\r\\n - `source:/some/file@head` - link explicitly to the latest revision of the file\\r\\n\\r\\nIf the revision is specified, one can even link to a specific line number:\\r\\n - `source:/some/file@123#L10`\\r\\n - `source:/tag/0.10@head#L10`\\r\\n\\r\\nFinally, one can also highlight an arbitrary set of lines:\\r\\n - `source:/some/file@123:10-20,100,103#L99` - highlight lines 10 to 20, and lines 100 and 103.\\r\\n   ''''(since 0.11)''''\\r\\n\\r\\nNote that in presence of multiple repositories, the name of the repository is simply integrated in the path you specify for `source:` (e.g. `source:reponame/trunk/README`). ''''(since 0.12)''''\\r\\n\\r\\n==== export: links ====\\r\\n\\r\\nTo force the download of a file in the repository, as opposed to displaying it in the browser, use the `export` link.  Several forms are available:\\r\\n * `export:/some/file` - get the HEAD revision of the specified file\\r\\n * `export:123:/some/file` - get revision 123 of the specified file\\r\\n * `export:/some/file@123` - get revision 123 of the specified file\\r\\n\\r\\nThis can be very useful for displaying XML or HTML documentation with correct stylesheets and images, in case that has been checked in into the repository. Note that for this use case, you''d have to allow the web browser to render the content by setting `[browser] render_unsafe_content = yes` (see TracIni#browser-section), otherwise Trac will force the files to be downloaded as attachments for security concerns. \\r\\n\\r\\nIf the path is to a directory in the repository instead of a specific file, the source browser will be used to display the directory (identical to the result of `source:/some/dir`).\\r\\n\\r\\n==== log: links ====\\r\\n\\r\\nThe `log:` links are used to display revision ranges. In its simplest form, it can link to the latest revisions of the specified path, but it can also support displaying an arbitrary set of revisions.\\r\\n - `log:/` - the latest revisions starting at the root of the repository\\r\\n - `log:/trunk/tools` - the latest revisions in `trunk/tools`\\r\\n - `log:/trunk/tools@10000` - the revisions in `trunk/tools` starting from  revision 10000\\r\\n - `log:@20788,20791:20795` - list revision 20788 and the revisions from 20791 to 20795 \\r\\n - `log:/trunk/tools@20788,20791:20795` - list revision 20788 and the revisions from 20791 to 20795 which affect the given path\\r\\n\\r\\nThere are short forms for revision ranges as well:\\r\\n - `[20788,20791:20795]`\\r\\n - `[20788,20791:20795/trunk/tools]`\\r\\n - `r20791:20795` (but not `r20788,20791:20795` nor `r20791:20795/trunk`)\\r\\n\\r\\nFinally, note that in all of the above, a revision range can be written either as `x:y` or `x-y`.\\r\\n\\r\\nIn the presence of multiple repositories, the name of the repository should be specified as the first part of the path, e.g. `log:repos/branches` or `[20-40/repos]`.\\r\\n\\r\\n----\\r\\nSee also: WikiFormatting, TracWiki, WikiPageNames, InterTrac, InterWiki\\r\\n \\r\\n', NULL, NULL),\n('TracRoadmap', 1, 1362994208300458, 'trac', '127.0.0.1', '= The Trac Roadmap =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nThe roadmap provides a view on the [wiki:TracTickets ticket system] that helps planning and managing the future development of a project.\\r\\n\\r\\n== The Roadmap View ==\\r\\n\\r\\nBasically, the roadmap is just a list of future milestones. You can add a description to milestones (using WikiFormatting) describing main objectives, for example. In addition, tickets targeted for a milestone are aggregated, and the ratio between active and resolved tickets is displayed as a milestone progress bar.  It is possible to further [trac:TracRoadmapCustomGroups customise the ticket grouping] and have multiple ticket statuses shown on the progress bar.\\r\\n\\r\\nThe roadmap can be filtered to show or hide ''''completed milestones'''' and ''''milestones with no due date''''. In the case that both ''''show completed milestones'''' and ''''hide milestones with no due date'''' are selected, ''''completed'''' milestones with no due date __will__ be shown.\\r\\n\\r\\n== The Milestone View ==\\r\\n\\r\\nYou can add a description for each milestone (using WikiFormatting) describing main objectives, for example. In addition, tickets targeted for a milestone are aggregated, and the ratio between active and resolved tickets is displayed as a milestone progress bar.  It is possible to further [trac:TracRoadmapCustomGroups customise the ticket grouping] and have multiple ticket statuses shown on the progress bar.\\r\\n\\r\\nIt is possible to drill down into this simple statistic by viewing the individual milestone pages. By default, the active/resolved ratio will be grouped and displayed by component. You can also regroup the status by other criteria, such as ticket owner or severity. Ticket numbers are linked to [wiki:TracQuery custom queries] listing corresponding tickets.\\r\\n\\r\\n== Roadmap Administration ==\\r\\n\\r\\nWith appropriate permissions it is possible to add, modify and remove milestones using either the web interface (roadmap and milestone pages), web administration interface or by using `trac-admin`. \\r\\n\\r\\n''''''Note:'''''' Milestone descriptions can not currently be edited using ''trac-admin''.\\r\\n\\r\\n== iCalendar Support ==\\r\\n\\r\\nThe Roadmap supports the [http://www.ietf.org/rfc/rfc2445.txt iCalendar] format to keep track of planned milestones and related tickets from your favorite calendar software. Many calendar applications support the iCalendar specification including\\r\\n * [http://www.apple.com/ical/ Apple iCal] for Mac OS X\\r\\n * the cross-platform [http://www.mozilla.org/projects/calendar/ Mozilla Calendar]\\r\\n * [http://chandlerproject.org Chandler]\\r\\n * [http://kontact.kde.org/korganizer/ Korganizer] (the calendar application of the [http://www.kde.org/ KDE] project)\\r\\n * [http://www.novell.com/de-de/products/desktop/features/evolution.html Evolution] also support iCalendar\\r\\n * [http://office.microsoft.com/en-us/outlook/ Microsoft Outlook] can also read iCalendar files (it appears as a new static calendar in Outlook)\\r\\n * [https://www.google.com/calendar/ Google Calendar] \\r\\n\\r\\nTo subscribe to the roadmap, copy the iCalendar link from the roadmap (found at the bottom of the page) and choose the \"Subscribe to remote calendar\" action (or similar) of your calendar application, and insert the URL just copied.\\r\\n\\r\\n''''''Note:'''''' For tickets to be included in the calendar as tasks, you need to be logged in when copying the link. You will only see tickets assigned to yourself, and associated with a milestone.\\r\\n\\r\\n''''''Note:'''''' To include the milestones in Google Calendar you might need to rewrite the URL.\\r\\n{{{\\r\\nRewriteEngine on\\r\\nRewriteRule ([^/.]+)/roadmap/([^/.]+)/ics /$1/roadmap?user=$2&format=ics\\r\\n}}}\\r\\n\\r\\nMore information about iCalendar can be found at [http://en.wikipedia.org/wiki/ICalendar Wikipedia].\\r\\n----\\r\\nSee also: TracTickets, TracReports, TracQuery, [trac:TracRoadmapCustomGroups]\\r\\n', NULL, NULL),\n('TracAdmin', 1, 1362994208302060, 'trac', '127.0.0.1', '= TracAdmin =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nTrac is distributed with a powerful command-line configuration tool. This tool can be used  to configure and customize your Trac-installation to better fit your needs.\\r\\n\\r\\nSome of those operations can also be performed via the ''''Admin'''' web interface, an updated version of the [trac:WebAdmin] plugin now integrated within Trac (since version 0.11).\\r\\n\\r\\n== Usage ==\\r\\n\\r\\nFor nearly every `trac-admin` command, you''ll need to specify the path to the TracEnvironment that you want to administer as the first argument, for example:\\r\\n{{{\\r\\ntrac-admin /path/to/projenv wiki list\\r\\n}}}\\r\\n\\r\\nThe only exception is for the `help` command, but even in this case if you omit the environment, you''ll only get a very succinct list of commands (`help` and `initenv`), the same list you''d get when invoking `trac-admin` alone.\\r\\nAlso, `trac-admin --version` will tell you about the Trac version (e.g. 0.12) corresponding to the program.\\r\\n\\r\\nIf you want to get a comprehensive list of the available commands and sub-commands, you need to specify an existing environment:\\r\\n{{{\\r\\ntrac-admin /path/to/projenv help\\r\\n}}}\\r\\n\\r\\nSome commands have a more detailed help, which you can access by specifying the command''s name as a subcommand for `help`:\\r\\n\\r\\n{{{\\r\\ntrac-admin /path/to/projenv help <command>\\r\\n}}}\\r\\n\\r\\n=== `trac-admin <targetdir> initenv` === #initenv\\r\\n\\r\\nThis subcommand is very important as it''s the one used to create a TracEnvironment in the specified `<targetdir>`. That directory must not exist prior to the call.\\r\\n\\r\\n[[TracAdminHelp(initenv)]]\\r\\n\\r\\nIt supports an extra `--inherit` option, which can be used to specify a global configuration file which can be used share settings between several environments. You can also inherit from a shared configuration afterwards, by setting the `[inherit] file` option in the `conf/trac.ini` file in your newly created environment, but the advantage of specifying the inherited configuration file at environment creation time is that only the options ''''not'''' already specified in the global configuration file will be written in the created environment''s `conf/trac.ini` file.\\r\\nSee TracIni#GlobalConfiguration.\\r\\n\\r\\nNote that in version 0.11 of Trac, `initenv` lost an extra last argument `<templatepath>`, which was used in previous versions to point to the `templates` folder. If you are using the one-liner ''`trac-admin /path/to/trac/ initenv <projectname> <db> <repostype> <repospath>`'' in the above and getting an error that reads ''''''''`Wrong number of arguments to initenv: 4`'''''''', then this is because you''re using a `trac-admin` script from an ''''''older'''''' version of Trac.\\r\\n\\r\\n== Interactive Mode ==\\r\\n\\r\\nWhen passing the environment path as the only argument, `trac-admin` starts in interactive mode.\\r\\nCommands can then be executed on the selected environment using the prompt, which offers tab-completion\\r\\n(on non-Windows environments, and when the Python `readline` module is available) and automatic repetition of the last command issued.\\r\\n\\r\\nOnce you''re in interactive mode, you can also get help on specific commands or subsets of commands:\\r\\n\\r\\nFor example, to get an explanation of the `resync` command, run:\\r\\n{{{\\r\\n> help resync\\r\\n}}}\\r\\n\\r\\nTo get help on all the Wiki-related commands, run:\\r\\n{{{\\r\\n> help wiki\\r\\n}}}\\r\\n\\r\\n== Full Command Reference ==\\r\\n\\r\\nYou''ll find below the detailed help for all the commands available by default in `trac-admin`. Note that this may not match the list given by `trac-admin <yourenv> help`, as the commands  pertaining to components disabled in that environment won''t be available and conversely some plugins activated in the environment can add their own commands.\\r\\n\\r\\n[[TracAdminHelp()]]\\r\\n\\r\\n----\\r\\nSee also: TracGuide, TracBackup, TracPermissions, TracEnvironment, TracIni, [trac:TracMigrate TracMigrate]\\r\\n', NULL, NULL);\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('TracUpgrade', 1, 1362994208303649, 'trac', '127.0.0.1', '= Upgrade Instructions =\\r\\n[[TracGuideToc]]\\r\\n[[PageOutline(2-4,,inline,unnumbered)]]\\r\\n\\r\\n== Instructions ==\\r\\n\\r\\nTypically, there are seven steps involved in upgrading to a newer version of Trac:\\r\\n\\r\\n=== 1. Bring your server off-line\\r\\n\\r\\nIt is not a good idea to update a running server: the server processes may have parts of the current packages cached in memory, and updating the code will likely trigger [#ZipImportError internal errors]. \\r\\n\\r\\n=== 2. Update the Trac Code === #UpdatetheTracCode\\r\\n\\r\\nGet the new version as described in TracInstall, or your operating system specific procedure.\\r\\n\\r\\nIf you already have a 0.11 version of Trac installed via `easy_install`, it might be easiest to also use `easy_install` to upgrade your Trac installation:\\r\\n\\r\\n{{{\\r\\n# easy_install --upgrade Trac==0.12\\r\\n}}}\\r\\n\\r\\nIf you do a manual (not operating system-specific) upgrade, you should also stop any running Trac servers before the installation. Doing \"hot\" upgrades is not advised, especially on Windows ([trac:#7265]).\\r\\n\\r\\nYou may also want to remove the pre-existing Trac code by deleting the `trac` directory from the Python `lib/site-packages` directory, or remove Trac `.egg` files from former versions.\\r\\nThe location of the site-packages directory depends on the operating system and the location in which Python was installed. However, the following locations are typical:\\r\\n * on Linux: `/usr/lib/python2.X/site-packages`\\r\\n * on Windows: `C:\\\\Python2.X\\\\lib\\\\site-packages`\\r\\n * on MacOSX: `/Library/Python/2.X/site-packages`\\r\\n\\r\\nYou may also want to remove the Trac `cgi-bin`, `htdocs`, `templates` and `wiki-default` directories that are commonly found in a directory called `share/trac`. (The exact location depends on your platform.)\\r\\n\\r\\nThis cleanup is not mandatory, but makes it easier to troubleshoot issues later on, as you won''t waste your time looking at code or templates from a previous release that are not being used anymore... As usual, make a backup before actually deleting things.\\r\\n\\r\\n=== 3. Upgrade the Trac Environment === #UpgradetheTracEnvironment\\r\\n\\r\\nEnvironment upgrades are not necessary for minor version releases unless otherwise noted. \\r\\n\\r\\nAfter restarting, Trac should show the instances which need a manual upgrade via the automated upgrade scripts to ease the pain. These scripts are run via [TracAdmin trac-admin]:\\r\\n{{{\\r\\ntrac-admin /path/to/projenv upgrade\\r\\n}}}\\r\\n\\r\\nThis command will do nothing if the environment is already up-to-date.\\r\\n\\r\\nNote that a backup of your database will be performed automatically prior to the upgrade. \\r\\nThis feature is relatively new for the PostgreSQL or MySQL database backends, so if it fails, you will have to backup the database manually. Then, to perform the actual upgrade, run:\\r\\n{{{\\r\\ntrac-admin /path/to/projenv upgrade --no-backup\\r\\n}}}\\r\\n\\r\\n=== 4. Update the Trac Documentation === #UpdatetheTracDocumentation\\r\\n\\r\\nEvery [TracEnvironment Trac environment] includes a copy of the Trac documentation for the installed version. As you probably want to keep the included documentation in sync with the installed version of Trac, [TracAdmin trac-admin] provides a command to upgrade the documentation:\\r\\n{{{\\r\\ntrac-admin /path/to/projenv wiki upgrade\\r\\n}}}\\r\\n\\r\\nNote that this procedure will leave your `WikiStart` page intact.\\r\\n\\r\\n\\r\\n=== 5. Refresh static resources ===\\r\\n\\r\\nIf you have set up a web server to give out static resources directly (accessed using the `/chrome/` URL) then you will need to refresh them using the same command:\\r\\n{{{\\r\\ntrac-admin /path/to/env deploy /deploy/path\\r\\n}}}\\r\\nthis will extract static resources and CGI scripts (`trac.wsgi`, etc) from new Trac version and its plugins into `/deploy/path`.\\r\\n\\r\\nSome web browsers (IE, Opera) cache CSS and Javascript files aggressively, so you may need to instruct your users to manually erase the contents of their browser''s cache, a forced refreshed (`<F5>`) should be enough.\\r\\n{{{#!comment\\r\\nRemove above note once #9936 is fixed.\\r\\n}}}\\r\\n\\r\\n=== 6. Steps specific to a given Trac version  ===\\r\\n==== Upgrading from Trac 0.12 to Trac 1.0 ==== #to1.0\\r\\n\\r\\nThe Trac components for Subversion support are no longer enabled by default. To enable the svn support, you need to make sure the `tracopt.versioncontrol.svn` components are enabled, for example by setting the following in the TracIni:\\r\\n{{{\\r\\n[components]\\r\\ntracopt.versioncontrol.svn.* = enabled\\r\\n}}}\\r\\nThe upgrade procedure should take care of this and change the TracIni appropriately, unless you already had the svn components explicitly disabled.\\r\\n\\r\\nAnother step in the automatic upgrade will change the way the attachments are stored. If you''re a bit paranoid, you might want to take a backup of the `attachments` directory before upgrading (but if you are, you already did a full copy of the environment, no?). In case the `attachments` directory contains some files which are //not// attachments, the last step of the migration to the new layout will fail: the deletion of the now unused `attachments` directory can''t be done if there are still files and folders in it. You may ignore this error, but better go have a look to these files, move them elsewhere and remove the `attachments` directory manually to cleanup the environment. The attachments themselves are now all located in your environment below the `files/attachments` directory.\\r\\n\\r\\n\\r\\n==== Upgrading from Trac 0.11 to Trac 0.12 ====\\r\\n\\r\\n===== Python 2.3 no longer supported =====\\r\\nThe minimum supported version of python is now 2.4\\r\\n\\r\\n===== SQLite v3.x required =====\\r\\nSQLite v2.x is no longer supported. If you still use a Trac database of this format, you''ll need to convert it to SQLite v3.x first. See [trac:PySqlite#UpgradingSQLitefrom2.xto3.x] for details.\\r\\n\\r\\n===== PySqlite 2 required =====\\r\\nPySqlite 1.1.x is no longer supported. Please install 2.5.5 or later if possible (see [#Tracdatabaseupgrade Trac database upgrade] below).\\r\\n\\r\\n===== Multiple Repository Support =====\\r\\nThe latest version includes support for multiple repositories. If you plan to add more repositories to your Trac instance, please refer to TracRepositoryAdmin#Migration.\\r\\n\\r\\nThis may be of interest to users with only one repository, since there''s now a way to avoid the potentially costly resync check at every request.\\r\\n\\r\\n===== Resynchronize the Trac Environment Against the Source Code Repository =====\\r\\n\\r\\nEach [TracEnvironment Trac environment] must be resynchronized against the source code repository in order to avoid errors such as \"[http://trac.edgewall.org/ticket/6120 No changeset ??? in the repository]\" while browsing the source through the Trac interface:\\r\\n\\r\\n{{{\\r\\ntrac-admin /path/to/projenv repository resync ''*''\\r\\n}}}\\r\\n\\r\\n===== Improved repository synchronization =====\\r\\nIn addition to supporting multiple repositories, there is now a more efficient method for synchronizing Trac and your repositories.\\r\\n\\r\\nWhile you can keep the same synchronization as in 0.11 adding the post-commit hook as outlined in TracRepositoryAdmin#Synchronization and TracRepositoryAdmin#ExplicitSync will allow more efficient synchronization and is more or less required for multiple repositories.\\r\\n\\r\\nNote that if you were using the `trac-post-commit-hook`, ''''you''re strongly advised to upgrade it'''' to the new hook documented in the above references and [TracWorkflow#Howtocombinethetracopt.ticket.commit_updaterwiththetestingworkflow here], as the old hook will not work with anything else than the default repository and even for this case, it won''t trigger the appropriate notifications.\\r\\n\\r\\n===== Authz permission checking =====\\r\\nThe authz permission checking has been migrated to a fine-grained permission policy. If you use authz permissions (aka `[trac] authz_file` and `authz_module_name`), you must add `AuthzSourcePolicy` in front of your permission policies in `[trac] permission_policies`. You must also remove `BROWSER_VIEW`, `CHANGESET_VIEW`, `FILE_VIEW` and `LOG_VIEW` from your global permissions (with `trac-admin $ENV permission remove` or the \"Permissions\" admin panel).\\r\\n\\r\\n===== Microsecond timestamps =====\\r\\nAll timestamps in database tables (except the `session` table) have been changed from \"seconds since epoch\" to \"microseconds since epoch\" values. This change should be transparent to most users, except for custom reports. If any of your reports use date/time columns in calculations (e.g. to pass them to `datetime()`), you must divide the values retrieved from the database by 1''000''000. Similarly, if a report provides a calculated value to be displayed as a date/time (i.e. with a column named \"time\", \"datetime\", \"changetime\", \"date\", \"created\" or \"modified\"), you must provide a microsecond timestamp, that is, multiply your previous calculation with 1''000''000.\\r\\n\\r\\n==== Upgrading from Trac 0.10 to Trac 0.11 ====\\r\\n===== Site Templates and Styles =====\\r\\nThe templating engine has changed in 0.11 to Genshi, please look at TracInterfaceCustomization for more information.\\r\\n\\r\\nIf you are using custom CSS styles or modified templates in the `templates` directory of the TracEnvironment, you will need to convert them to the Genshi way of doing things. To continue to use your style sheet, follow the instructions at TracInterfaceCustomization#SiteAppearance.\\r\\n\\r\\n===== Trac Macros, Plugins =====\\r\\nThe Trac macros will need to be adapted, as the old-style wiki-macros are not supported anymore (due to the drop of [trac:ClearSilver] and the HDF); they need to be converted to the new-style macros, see WikiMacros. When they are converted to the new style, they need to be placed into the plugins directory instead and not wiki-macros, which is no longer scanned for macros or plugins.\\r\\n\\r\\n===== For FCGI/WSGI/CGI users =====\\r\\nFor those who run Trac under the CGI environment, run this command in order to obtain the trac.*gi file:\\r\\n{{{\\r\\ntrac-admin /path/to/env deploy /deploy/directory/path\\r\\n}}}\\r\\n\\r\\nThis will create a deploy directory with the following two subdirectories: `cgi-bin` and `htdocs`. Then update your Apache configuration file `httpd.conf` with this new `trac.cgi` location and `htdocs` location.\\r\\n\\r\\n===== Web Admin plugin integrated =====\\r\\nIf you had the webadmin plugin installed, you can uninstall it as it is part of the Trac code base since 0.11.\\r\\n\\r\\n=== 7. Restart the Web Server === #RestarttheWebServer\\r\\n\\r\\nIf you are not running [wiki:TracCgi CGI], reload the new Trac code by restarting your web server.\\r\\n\\r\\n== Known Issues ==\\r\\n\\r\\nThings you should pay attention to, while upgrading.\\r\\n\\r\\n=== Customized Templates\\r\\n\\r\\nTrac supports customization of its Genshi templates by placing copies of the templates in the `<env>/templates` folder of your [TracEnvironment environment] or in a common location specified in the [[TracIni#GlobalConfiguration| [inherit] templates_dir]] configuration setting. If you choose to do so, be wary that you will need to repeat your changes manually on a copy of the new templates when you upgrade to a new release of Trac (even a minor one), as the templates will likely evolve. So keep a diff around ;-)\\r\\n\\r\\nThe preferred way to perform TracInterfaceCustomization is to write a custom plugin doing an appropriate `ITemplateStreamFilter` transformation, as this is more robust in case of changes: we usually won''t modify element `id`s or change CSS `class`es, and if we have to do so, this will be documented in the TracDev/ApiChanges pages.\\r\\n\\r\\n=== !ZipImportError ===\\r\\n\\r\\nDue to internal caching of zipped packages,  whenever the content of the packages change on disk, the in-memory zip index will no longer match and you''ll get irrecoverable !ZipImportError errors. Better anticipate and bring your server down for maintenance before upgrading.\\r\\nSee [trac:#7014] for details.\\r\\n\\r\\n=== Wiki Upgrade ===\\r\\n`trac-admin` will not delete or remove default wiki pages that were present in a previous version but are no longer in the new version.\\r\\n\\r\\n=== Trac database upgrade ===\\r\\n\\r\\nA known issue in some versions of PySqlite (2.5.2-2.5.4) prevents the trac-admin upgrade script from successfully upgrading the database format. It is advised to use either a newer or older version of the sqlite python bindings to avoid this error. For more details see ticket [trac:#9434].\\r\\n\\r\\n=== parent dir ===\\r\\nIf you use a trac parent env configuration and one of the plugins in one child does not work, none of the children work.\\r\\n\\r\\n== Related topics\\r\\n\\r\\n=== Upgrading Python ===\\r\\n\\r\\nUpgrading Python to a newer version will require reinstallation of Python packages: Trac of course; also [http://pypi.python.org/pypi/setuptools easy_install], if you''ve been using that.  Assuming you''re using Subversion, you''ll also need to upgrade the Python bindings for svn.\\r\\n\\r\\n==== Windows and Python 2.6 ====\\r\\n\\r\\nIf you''ve been using !CollabNet''s Subversion package, you may need to uninstall that in favor of [http://alagazam.net/ Alagazam], which has the Python bindings readily available (see TracSubversion).  The good news is, that works with no tweaking.\\r\\n\\r\\n=== Changing Database Backend ===\\r\\n==== SQLite to PostgreSQL ====\\r\\n\\r\\nThe [http://trac-hacks.org/wiki/SqliteToPgScript sqlite2pg] script on [http://trac-hacks.org trac-hacks.org] has been written to assist in migrating a SQLite database to a PostgreSQL database\\r\\n\\r\\n=== Upgrading from older versions of Trac === #OlderVersions\\r\\n\\r\\nFor upgrades from versions older than Trac 0.10, refer first to [trac:wiki:0.10/TracUpgrade#SpecificVersions].\\r\\n\\r\\n-----\\r\\nSee also: TracGuide, TracInstall\\r\\n', NULL, NULL),\n('TracLogging', 1, 1362994208305818, 'trac', '127.0.0.1', '= Trac Logging =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nTrac supports logging of system messages using the standard [http://docs.python.org/library/logging.html logging module] that comes with Python.\\r\\n\\r\\nLogging is configured in the `[logging]` section in [wiki:TracIni#logging-section trac.ini].\\r\\n\\r\\n== Supported Logging Methods ==\\r\\n\\r\\nThe log method is set using the `log_type` option in [wiki:TracIni#logging-section trac.ini], which takes any of the following values:\\r\\n\\r\\n ''''''none'''':: Suppress all log messages.\\r\\n ''''''file'''''':: Log messages to a file, specified with the `log_file` option in [wiki:TracIni#logging-section trac.ini]. Relative paths in `log_file` are resolved relative to the `log` directory of the environment.\\r\\n ''''''stderr'''''':: Output all log entries to console ([wiki:TracStandalone tracd] only).\\r\\n ''''''syslog'''''':: (UNIX) Send all log messages to the local syslogd via named pipe `/dev/log`. By default, syslog will write them to the file /var/log/messages.\\r\\n ''''''eventlog'''''':: (Windows) Use the system''s NT Event Log for Trac logging.\\r\\n\\r\\n== Log Levels ==\\r\\n\\r\\nThe verbosity level of logged messages can be set using the `log_level` option in [wiki:TracIni#logging-section trac.ini]. The log level defines the minimum level of urgency required for a message to be logged, and those levels are:\\r\\n\\r\\n ''''''CRITICAL'''''':: Log only the most critical (typically fatal) errors.\\r\\n ''''''ERROR'''''':: Log failures, bugs and errors. \\r\\n ''''''WARN'''''':: Log warnings, non-interrupting events.\\r\\n ''''''INFO'''''':: Diagnostic information, log information about all processing.\\r\\n ''''''DEBUG'''''':: Trace messages, profiling, etc.\\r\\n\\r\\nNote that starting with Trac 0.11.5 you can in addition enable logging of SQL statements, at debug level. This is turned off by default, as it''s very verbose (set `[trac] debug_sql = yes` in TracIni to activate).\\r\\n\\r\\n== Log Format ==\\r\\n\\r\\nStarting with Trac 0.10.4 (see [trac:#2844 #2844]), it is possible to set the output format for log entries. This can be done through the `log_format` option in [wiki:TracIni#logging-section trac.ini]. The format is a string which can contain any of the [http://docs.python.org/library/logging.html#logrecord-attributes Python logging Formatter variables]. Additonally, the following Trac-specific variables can be used:\\r\\n ''''''$(basename)s'''''':: The last path component of the current environment.\\r\\n ''''''$(path)s'''''':: The absolute path for the current environment.\\r\\n ''''''$(project)s'''''':: The originating project''s name.\\r\\n\\r\\nNote that variables are identified using a dollar sign (`$(...)s`) instead of percent sign (`%(...)s`).\\r\\n\\r\\nThe default format is:\\r\\n{{{\\r\\nlog_format = Trac[$(module)s] $(levelname)s: $(message)s\\r\\n}}}\\r\\n\\r\\nIn a multi-project environment where all logs are sent to the same place (e.g. `syslog`), it makes sense to add the project name. In this example we use `basename` since that can generally be used to identify a project:\\r\\n{{{\\r\\nlog_format = Trac[$(basename)s:$(module)s] $(levelname)s: $(message)s\\r\\n}}}\\r\\n\\r\\n----\\r\\nSee also: TracIni, TracGuide, TracEnvironment', NULL, NULL),\n('TracTickets', 1, 1362994208307778, 'trac', '127.0.0.1', '= The Trac Ticket System =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nThe Trac ticket database provides simple but effective tracking of issues and bugs within a project.\\r\\n\\r\\nAs the central project management element of Trac, tickets can be used for ''''''project tasks'''''', ''''''feature requests'''''', ''''''bug reports'''''', ''''''software support issues'''''' among others. \\r\\n\\r\\nAs with the TracWiki, this subsystem has been designed with the goal of making user contribution and participation as simple as possible. It should be as easy as possible to report bugs, ask questions, suggest improvements and discuss resolutions.\\r\\n\\r\\nAn issue is assigned to a person who must resolve it or reassign the ticket to someone else.\\r\\nAll tickets can be edited, annotated, assigned, prioritized and discussed at any time.\\r\\n\\r\\n[=#edit-permissions]\\r\\nHowever, some Trac installations may put restrictions in place about who can change what. For example, the default installation doesn''t permit to non-authenticated users (\"anonymous\" users) to change anything, even to comment on an issue, for obvious spam prevention reasons. Check the local contributing policy, which you can usually find on the front page WikiStart, or contact your local Trac administrator.\\r\\n\\r\\n== Ticket Fields ==\\r\\n\\r\\nA  ticket contains the following information attributes:\\r\\n \\r\\n * ''''''Reporter'''''' — The author of the ticket.\\r\\n * ''''''Type'''''' — The nature of the ticket (for example, defect or enhancement request). See TicketTypes for more details.\\r\\n * ''''''Component'''''' — The project module or subsystem this ticket concerns.\\r\\n * ''''''Version'''''' — Version of the project that this ticket pertains to.\\r\\n * ''''''Keywords'''''' — Keywords that a ticket is marked with. Useful for searching and report generation.\\r\\n * ''''''Priority'''''' — The importance of this issue, ranging from ''''trivial'''' to ''''blocker''''. A pull-down if different priorities where defined.\\r\\n * ''''''Milestone'''''' — When this issue should be resolved at the latest. A pull-down menu containing a list of milestones.\\r\\n * ''''''Assigned to/Owner'''''' — Principal person responsible for handling the issue.\\r\\n * ''''''Cc'''''' — A comma-separated list of other users or E-Mail addresses to notify. ''''Note that this does not imply responsiblity or any other policy.''''\\r\\n * ''''''Resolution'''''' — Reason for why a ticket was closed. One of {{{fixed}}}, {{{invalid}}}, {{{wontfix}}}, {{{duplicate}}}, {{{worksforme}}}.\\r\\n * ''''''Status'''''' — What is the current status? One of {{{new}}}, {{{assigned}}}, {{{closed}}}, {{{reopened}}}.\\r\\n * ''''''Summary'''''' — A brief description summarizing the problem or issue. Simple text without WikiFormatting.\\r\\n * ''''''Description'''''' — The body of the ticket. A good description should be specific, descriptive and to the point. Accepts WikiFormatting.\\r\\n\\r\\n''''''Notes:'''''' \\r\\n - Versions of Trac prior to 0.9 did not have the ''''type'''' field, but instead provided a ''''severity'''' field and different default values for the ''''priority'''' field. This change was done to simplify the ticket model by removing the somewhat blurry distinction between ''''priority'''' and ''''severity''''. However, the old model is still available if you prefer it: just add/modify the default values of the ''''priority'''' and ''''severity'''', and optionally hide the ''''type'''' field by removing all the possible values through [wiki:TracAdmin trac-admin].\\r\\n\\r\\n - the [trac:TicketTypes type], [trac:TicketComponent component], version, priority and severity fields can be managed with [wiki:TracAdmin trac-admin] or with the [trac:WebAdmin WebAdmin] plugin.\\r\\n\\r\\n - Description of the builtin ''''priority'''' values is available at [trac:TicketTypes#Whyistheseverityfieldgone TicketTypes]\\r\\n\\r\\n== Changing and Commenting Tickets ==\\r\\n\\r\\nWith appropriate permissions, as already mentioned [#edit-permissions above], a ticket entered into Trac can at any time be modified by ''''''annotating''''''.\\r\\n\\r\\nThen, annotations like changes and comments to the ticket are logged as a part of the ticket itself. When viewing a ticket, the history of changes will appear below the main ticket area.\\r\\n\\r\\nComment editing (available since 0.12) is meant to be used to make small corrections to comments, like fixing formatting, forgotten WikiFormatting or spelling errors, not major edits. For longer edits, you should be adding a new comment instead. Editing a comment will not produce a new entry on [/timeline] while entering a new comment or other changes will do.\\r\\n\\r\\nAll edits (field changes, new comments, comment edits) update the \"last changed\" time of the ticket.\\r\\n\\r\\n\\r\\n''''''Notes:'''''' \\r\\n - An important feature is being able to use TracLinks and WikiFormatting in ticket descriptions and comments. Use TracLinks to refer to other issues, changesets or files to make your ticket more specific and easier to understand.\\r\\n\\r\\n - See TracNotification for how to configure email notifications of ticket changes.\\r\\n\\r\\n - See TracWorkflow for information about the state transitions (ticket lifecycle), and how this workflow can be customized.\\r\\n\\r\\n== Default Values for Drop-Down Fields ==\\r\\n\\r\\nThe option selected by default for the various drop-down fields can be set in [wiki:TracIni trac.ini], in the `[ticket]` section:\\r\\n\\r\\n * `default_component`: Name of the component selected by default\\r\\n * `default_milestone`: Name of the default milestone\\r\\n * `default_priority`: Default priority value\\r\\n * `default_severity`: Default severity value\\r\\n * `default_type`: Default ticket type\\r\\n * `default_version`: Name of the default version\\r\\n * `default_owner`: Name of the default owner. If set to the text \"< default >\" (the default value), the component owner is used.\\r\\n\\r\\nIf any of these options are omitted, the default value will either be the first in the list, or an empty value, depending on whether the field in question is required to be set.  Some of these can be chosen through the [trac:WebAdmin WebAdmin] plugin in the \"Ticket System\" section (others in the [[wiki:TracIni#ticket-section|\"[ticket]\"]] section in `trac.ini`).\\r\\n\\r\\n\\r\\n== Hiding Fields and Adding Custom Fields ==\\r\\n\\r\\nMany of the default ticket fields can be hidden from the ticket web interface simply by removing all the possible values through [wiki:TracAdmin trac-admin]. This of course only applies to drop-down fields, such as ''''type'''', ''''priority'''', ''''severity'''', ''''component'''', ''''version'''' and ''''milestone''''.\\r\\n\\r\\nTrac also lets you add your own custom ticket fields. See TracTicketsCustomFields for more information.\\r\\n\\r\\n\\r\\n== Assign-to as Drop-Down List ==\\r\\n\\r\\nIf the list of possible ticket owners is finite, you can change the ''''assign-to'''' ticket field from a text input to a drop-down list. This is done by setting the `restrict_owner` option of the `[ticket]` section in [wiki:TracIni trac.ini] to “true”. In that case, Trac will use the list of all users who have accessed the project to populate the drop-down field.\\r\\n\\r\\nTo appear in the dropdown list, a user needs be registered with the project, ''''i.e.'''' a user session should exist in the database. Such an entry is automatically created in the database the first time the user submits a change in the project, for example when editing the user''s details in the ''''Settings'''' page, or simply by authenticating if the user has a login. Also, the user must have `TICKET_MODIFY` [TracPermissions permissions].\\r\\n\\r\\n''''''Notes:'''''' \\r\\n - See [http://pacopablo.com/wiki/pacopablo/blog/set-assign-to-drop-down Populating Assign To Drop Down] on how to add user entries at database level\\r\\n\\r\\n - If you need serious flexibility and aren''t afraid of a little plugin coding of your own, see [http://trac-hacks.org/wiki/FlexibleAssignToPlugin FlexibleAssignTo] (disclosure: I''m the author)\\r\\n\\r\\n -  Activating this option may cause some performance degradation, read more about this in the [trac:TracPerformance#Configuration Trac performance] page.\\r\\n\\r\\n== Preset Values for New Tickets ==\\r\\n\\r\\nTo create a link to the new-ticket form filled with preset values, you need to call the `/newticket?` URL with `variable=value` separated by `&`. \\r\\n\\r\\nPossible variables are :\\r\\n\\r\\n * ''''''type'''''' — The type droplist\\r\\n * ''''''reporter'''''' — Name or email of the reporter\\r\\n * ''''''summary'''''' — Summary line for the ticket\\r\\n * ''''''description'''''' — Long description of the ticket\\r\\n * ''''''component'''''' — The component droplist\\r\\n * ''''''version'''''' — The version droplist\\r\\n * ''''''severity'''''' — The severity droplist\\r\\n * ''''''keywords'''''' — The keywords \\r\\n * ''''''priority'''''' — The priority droplist\\r\\n * ''''''milestone'''''' — The milestone droplist\\r\\n * ''''''owner'''''' — The person responsible for the ticket\\r\\n * ''''''cc'''''' — The list of emails for notifying about the ticket change\\r\\n\\r\\nExample: ''''`[/newticket?summary=Compile%20Error&version=1.0&component=gui]`''''[[BR]]\\r\\n\\r\\n----\\r\\nSee also:  TracGuide, TracWiki, TracTicketsCustomFields, TracNotification, TracReports, TracQuery\\r\\n', NULL, NULL),\n('TracWiki', 1, 1362994208309431, 'trac', '127.0.0.1', '\\r\\n= The Trac Wiki System =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nTrac has a built-in wiki system which you can use for organizing knowledge and information in a very flexible way by [WikiNewPage creating pages] containing an intuitive and easy to learn textual markup. This text markup is also used in all other parts of the system, not only in [wiki:TitleIndex wiki pages], but also in [wiki:TracTickets ticket] description and comments, [wiki:TracChangeset check-in log messages], [wiki:TracRoadmap milestone] descriptions and [wiki:TracReports report] descriptions, even in third-party extensions.\\r\\nIt allows for formatted text and hyperlinks in and between all Trac modules.\\r\\n\\r\\nEditing wiki text is easy, using any web browser and a simple [WikiFormatting formatting system], rather than more complex markup languages like HTML.  The reasoning behind its design is that HTML, with its large collection of nestable tags, is too complicated to allow fast-paced editing, and distracts from the actual content of the pages. Note though that Trac also supports [WikiHtml HTML], [WikiRestructuredText reStructuredText] and [http://www.textism.com/tools/textile/ Textile] as alternative markup formats, which can eventually be used in parts of a page (so called wiki “blocks”).\\r\\n\\r\\nThe main goal of the wiki is to make editing text easier and ''''encourage'''' people to contribute and annotate text content for a project. Trac also provides a simple toolbar to make formatting text even easier, and supports the [http://universaleditbutton.org/Universal_Edit_Button universal edit button] of your browser.\\r\\n\\r\\nThe wiki itself does not enforce any structure, but rather resembles a stack of empty sheets of paper, where you can organize information and documentation as you see fit, and later reorganize if necessary. \\r\\nAs contributing to a wiki is essentially building hypertext, \\r\\ngeneral advice regarding HTML authoring apply here as well.\\r\\nFor example, the ''''[http://www.w3.org/Provider/Style Style Guide for online hypertext]'''' explains how to think about the\\r\\n[http://www.w3.org/Provider/Style/Structure.html overall structure of a work] \\r\\nand how to organize information [http://www.w3.org/Provider/Style/WithinDocument.html within each document]. One of the most important tip is “make your HTML page such that you can read it even if you don''t follow any links.”\\r\\n\\r\\nLearn more about:\\r\\n * WikiFormatting rules, including advanced topics like WikiMacros and WikiProcessors\\r\\n * How to use WikiPageNames and other forms of TracLinks which are used to refer in a precise way to any resource within Trac\\r\\n\\r\\nIf you want to practice editing, please use the SandBox. Note that not all Trac wikis are editable by anyone, this depends on the local policy; check with your Trac administrators.\\r\\n\\r\\nBefore saving your changes, you can ''''Preview'''' the page or ''''Review the Changes'''' you''ve made.\\r\\nYou can get an automatic preview of the formatting as you type when you activate the ''''Edit Side-by-side'''' mode (you have to Preview the page for the setting to take effect). '''' There is a [wiki:/TracIni#trac-section configurable delay] between when you make your edit and when the automatic preview will update.''''\\r\\n\\r\\nSome more information about wikis on the web:\\r\\n * A definition of [http://wikipedia.org/wiki/Wiki Wiki], in a famous wiki encyclopedia\\r\\n * The [http://c2.com/cgi/wiki?WikiHistory History] of the original wiki\\r\\n * A wiki page explaining [http://www.usemod.com/cgi-bin/mb.pl?WhyWikiWorks why wiki works]\\r\\n\\r\\n----\\r\\nSee also: TracGuide\\r\\n', NULL, NULL),\n('WikiRestructuredTextLinks', 1, 1362994208310893, 'trac', '127.0.0.1', '= TracLinks in reStructuredText =\\r\\n\\r\\nThis document illustrates how to use the `:trac:` role in reStructuredText. The page is written like:\\r\\n\\r\\n{{{\\r\\n{{{\\r\\n#!rst \\r\\nExamples:\\r\\n\\r\\n * Tickets: :trac:`#1` or :trac:`ticket:1`\\r\\n * Ticket comments: :trac:`comment:ticket:1:2`\\r\\n * Reports: :trac:`{1}` or :trac:`report:1`\\r\\n * Changesets: :trac:`r1`, :trac:`[1]` or :trac:`changeset:1`\\r\\n * Revision log: :trac:`r1:3`, :trac:`[1:3]` or :trac:`log:@1:3`, :trac:`log:trunk@1:3`\\r\\n * Diffs (since version 0.10): :trac:`diff:@20:30`, :trac:`diff:tags/trac-0.9.2/wiki-default//tags/trac-0.9.3/wiki-default` or :trac:`diff:trunk/trac@3538//sandbox/vc-refactoring/trac@3539`\\r\\n * Wiki pages: :trac:`CamelCase` or :trac:`wiki:CamelCase`\\r\\n * Milestones: :trac:`milestone:1.0`\\r\\n * Attachment: :trac:`attachment:ticket:944:attachment.1073.diff`\\r\\n * Files: :trac:`source:trunk/COPYING`\\r\\n * A specific file revision: :trac:`source:/trunk/COPYING@200`\\r\\n * A particular line of a specific file revision: :trac:`source:/trunk/COPYING@200#L25`\\r\\n\\r\\nAn explicit label can be specified, separated from the link by a space:\\r\\n\\r\\n * See :trac:`#1 ticket 1` and the :trac:`source:trunk/COPYING license`.\\r\\n}}}\\r\\n}}}\\r\\n\\r\\nProvided you have docutils installed, the above block will render as:\\r\\n----\\r\\n{{{\\r\\n#!rst \\r\\nExamples:\\r\\n\\r\\n * Tickets: :trac:`#1` or :trac:`ticket:1`\\r\\n * Ticket comments: :trac:`comment:ticket:1:2`\\r\\n * Reports: :trac:`{1}` or :trac:`report:1`\\r\\n * Changesets: :trac:`r1`, :trac:`[1]` or :trac:`changeset:1`\\r\\n * Revision log: :trac:`r1:3`, :trac:`[1:3]` or :trac:`log:@1:3`, :trac:`log:trunk@1:3`\\r\\n * Diffs (since version 0.10): :trac:`diff:@20:30`, :trac:`diff:tags/trac-0.9.2/wiki-default//tags/trac-0.9.3/wiki-default` or :trac:`diff:trunk/trac@3538//sandbox/vc-refactoring/trac@3539`\\r\\n * Wiki pages: :trac:`CamelCase` or :trac:`wiki:CamelCase`\\r\\n * Milestones: :trac:`milestone:1.0`\\r\\n * Attachment: :trac:`attachment:ticket:944:attachment.1073.diff`\\r\\n * Files: :trac:`source:trunk/COPYING`\\r\\n * A specific file revision: :trac:`source:/trunk/COPYING@200`\\r\\n * A particular line of a specific file revision: :trac:`source:/trunk/COPYING@200#L25`\\r\\n\\r\\nAn explicit label can be specified, separated from the link by a space:\\r\\n\\r\\n * See :trac:`#1 ticket 1` and the :trac:`source:trunk/COPYING license`.\\r\\n}}}\\r\\n----\\r\\n\\r\\nNote also that any of the above could have been written using substitution references and the `trac::` directive:\\r\\n{{{\\r\\n{{{\\r\\n#!rst\\r\\nSee |ticket123|.\\r\\n\\r\\n .. |ticket123| trac:: ticket:123 this ticket\\r\\n}}}\\r\\n}}}\\r\\n\\r\\nThis renders as:\\r\\n----\\r\\n\\r\\n{{{\\r\\n#!rst\\r\\nSee |ticket123|.\\r\\n\\r\\n .. |ticket123| trac:: ticket:123 this ticket\\r\\n}}}\\r\\n\\r\\n----\\r\\nSee also: WikiRestructuredText, TracLinks', NULL, NULL),\n('TracEnvironment', 1, 1362994208312317, 'trac', '127.0.0.1', '= The Trac Environment =\\r\\n\\r\\nTrac uses a directory structure and a database for storing project data. The directory is referred to as the “environment”.\\r\\n\\r\\n== Creating an Environment ==\\r\\n\\r\\nA new Trac environment is created using  [TracAdmin#initenv trac-admin''s initenv]:\\r\\n{{{\\r\\n$ trac-admin /path/to/myproject initenv\\r\\n}}}\\r\\n\\r\\n`trac-admin` will ask you for the name of the project and the\\r\\ndatabase connection string (explained below).\\r\\n\\r\\n=== Some Useful Tips\\r\\n - The user under which the web server runs will require file system write permission to \\r\\n the environment directory and all the files inside. Please remember to set\\r\\n the appropriate permissions. The same applies to the source code repository, \\r\\n although the user under which Trac runs will only require write access to a Subversion repository created with the BDB file system; for other repository types, check the corresponding plugin''s documentation. \\r\\n \\r\\n - `initenv`, when using an svn repository, does not imply that trac-admin will perform `svnadmin create` for the specified repository path. You need to perform the `svnadmin create` prior to `trac-admin initenv` if you''re creating a new svn repository altogether with a new trac environment, otherwise you will see a message \"Warning: couldn''t index the repository\" when initializing the environment.\\r\\n\\r\\n - Non-ascii environment paths are not supported\\r\\n \\r\\n - Also, it seems that project names with spaces can be problematic for authentication (see [trac:#7163]).\\r\\n\\r\\n - TracPlugins located in a [TracIni#inherit-section shared plugins folder] that is defined in an [TracIni#GlobalConfiguration inherited configuration] are currently not loaded during creation, and hence, if they need to create extra tables for example, you''ll need to [TracUpgrade#UpgradetheTracEnvironment upgrade the environment] before being able to use it.\\r\\n\\r\\n== Database Connection Strings ==\\r\\n\\r\\nSince version 0.9, Trac supports both [http://sqlite.org/ SQLite] and\\r\\n[http://www.postgresql.org/ PostgreSQL] database backends.  Preliminary\\r\\nsupport for [http://mysql.com/ MySQL] was added in 0.10.  The default is\\r\\nto use SQLite, which is probably sufficient for most projects. The database\\r\\nfile is then stored in the environment directory, and can easily be \\r\\n[wiki:TracBackup backed up] together with the rest of the environment.\\r\\n\\r\\n=== SQLite Connection String ===\\r\\nThe connection string for an SQLite database is:\\r\\n{{{\\r\\nsqlite:db/trac.db\\r\\n}}}\\r\\nwhere `db/trac.db` is the path to the database file within the Trac environment.\\r\\n\\r\\n=== PostgreSQL Connection String ===\\r\\nIf you want to use PostgreSQL or MySQL instead, you''ll have to use a\\r\\ndifferent connection string. For example, to connect to a PostgreSQL\\r\\ndatabase on the same machine called `trac`, that allows access to the\\r\\nuser `johndoe` with the password `letmein`, use:\\r\\n{{{\\r\\npostgres://johndoe:letmein@localhost/trac\\r\\n}}}\\r\\n''''Note that due to the way the above string is parsed, the \"/\" and \"@\" characters cannot be part of the password.''''\\r\\n\\r\\nIf PostgreSQL is running on a non-standard port (for example 9342), use:\\r\\n{{{\\r\\npostgres://johndoe:letmein@localhost:9342/trac\\r\\n}}}\\r\\n\\r\\nOn UNIX, you might want to select a UNIX socket for the transport,\\r\\neither the default socket as defined by the PGHOST environment variable:\\r\\n{{{\\r\\npostgres://user:password@/database\\r\\n}}}\\r\\nor a specific one:\\r\\n{{{\\r\\npostgres://user:password@/database?host=/path/to/socket/dir\\r\\n}}}\\r\\n\\r\\nNote that with PostgreSQL you will have to create the database before running\\r\\n`trac-admin initenv`.\\r\\n\\r\\nSee the [http://www.postgresql.org/docs/ PostgreSQL documentation] for detailed instructions on how to administer [http://postgresql.org PostgreSQL].\\r\\nGenerally, the following is sufficient to create a database user named `tracuser`, and a database named `trac`.\\r\\n{{{\\r\\ncreateuser -U postgres -E -P tracuser\\r\\ncreatedb -U postgres -O tracuser -E UTF8 trac\\r\\n}}}\\r\\nWhen running `createuser` you will be prompted for the password for the user ''tracuser''. This new user will not be a superuser, will not be allowed to create other databases and will not be allowed to create other roles. These privileges are not needed to run a trac instance. If no password is desired for the user, simply remove the `-P` and `-E` options from the `createuser` command.  Also note that the database should be created as UTF8. LATIN1 encoding causes errors trac''s use of unicode in trac.  SQL_ASCII also seems to work.\\r\\n\\r\\nUnder some default configurations (debian) one will have run the `createuser` and `createdb` scripts as the `postgres` user.  For example:\\r\\n{{{\\r\\nsudo su - postgres -c ''createuser -U postgres -S -D -R -E -P tracuser''\\r\\nsudo su - postgres -c ''createdb -U postgres -O tracuser -E UTF8 trac''\\r\\n}}}\\r\\n\\r\\nTrac uses the `public` schema by default but you can specify a different schema in the connection string:\\r\\n{{{\\r\\npostgres://user:pass@server/database?schema=yourschemaname\\r\\n}}}\\r\\n\\r\\n=== MySQL Connection String ===\\r\\n\\r\\nIf you want to use MySQL instead, you''ll have to use a\\r\\ndifferent connection string. For example, to connect to a MySQL\\r\\ndatabase on the same machine called `trac`, that allows access to the\\r\\nuser `johndoe` with the password `letmein`, the mysql connection string is:\\r\\n{{{\\r\\nmysql://johndoe:letmein@localhost:3306/trac\\r\\n}}}\\r\\n\\r\\n== Source Code Repository ==\\r\\n\\r\\nSince version 0.12, a single Trac environment can be connected to more than one repository. There are many different ways to connect repositories to an environment, see TracRepositoryAdmin. This page also details the various attributes that can be set for a repository (like `type`, `url`, `description`).\\r\\n\\r\\nIn Trac 0.12 `trac-admin` no longer asks questions related to repositories. Therefore, by default Trac is not connected to any source code repository, and the ''''Browse Source'''' toolbar item will not be displayed.\\r\\nYou can also explicitly disable the `trac.versioncontrol.*` components (which are otherwise still loaded)\\r\\n{{{\\r\\n[components]\\r\\ntrac.versioncontrol.* = disabled\\r\\n}}}\\r\\n\\r\\nFor some version control systems, it is possible to specify not only the path to the repository,\\r\\nbut also a ''''scope'''' within the repository. Trac will then only show information\\r\\nrelated to the files and changesets below that scope. The Subversion backend for\\r\\nTrac supports this; for other types, check the corresponding plugin''s documentation.\\r\\n\\r\\nExample of a configuration for a Subversion repository used as the default repository:\\r\\n{{{\\r\\n[trac]\\r\\nrepository_type = svn\\r\\nrepository_dir = /path/to/your/repository\\r\\n}}}\\r\\n\\r\\nThe configuration for a scoped Subversion repository would be:\\r\\n{{{\\r\\n[trac]\\r\\nrepository_type = svn\\r\\nrepository_dir = /path/to/your/repository/scope/within/repos\\r\\n}}}\\r\\n\\r\\n== Directory Structure ==\\r\\n\\r\\nAn environment directory will usually consist of the following files and directories:\\r\\n\\r\\n * `README` - Brief description of the environment.\\r\\n * `VERSION` - Contains the environment version identifier.\\r\\n * `attachments` - Attachments to wiki pages and tickets are stored here.\\r\\n * `conf`\\r\\n   * `trac.ini` - Main configuration file. See TracIni.\\r\\n * `db`\\r\\n   * `trac.db` - The SQLite database (if you''re using SQLite).\\r\\n * `htdocs` - directory containing web resources, which can be referenced in Genshi templates using `/htdocs/site/...` URLs. ''''(since 0.11)''''\\r\\n * `log` - default directory for log files, if logging is turned on and a relative path is given.\\r\\n * `plugins` - Environment-specific [wiki:TracPlugins plugins] (Python eggs or single file plugins, since [trac:milestone:0.10 0.10])\\r\\n * `templates` - Custom Genshi environment-specific templates. ''''(since 0.11)''''\\r\\n   * `site.html` - method to customize header, footer, and style, described in TracInterfaceCustomization#SiteAppearance\\r\\n\\r\\n=== Caveat: don''t confuse a ''''Trac environment directory'''' with the ''''source code repository directory'''' #Caveat\\r\\n\\r\\nThis is a common beginners'' mistake.\\r\\nIt happens that the structure for a Trac environment is loosely modelled after the Subversion repository directory \\r\\nstructure, but those are two disjoint entities and they are not and ''''must not'''' be located at the same place.\\r\\n\\r\\n----\\r\\nSee also: TracAdmin, TracBackup, TracIni, TracGuide\\r\\n', NULL, NULL),\n('TracIni', 1, 1362994208314163, 'trac', '127.0.0.1', '= The Trac Configuration File =\\r\\n\\r\\n ''''[Note To Editors] Please discuss documentation changes in the [#Discussion] section. Even better, send us [TracDev/SubmittingPatches documentation patches] against the ''''code'''' (i.e. where the configuration entries are documented), either on Trac-dev or on new tickets. ''''\\r\\n\\r\\n[[TracGuideToc]]\\r\\n[[PageOutline]]\\r\\n\\r\\nTrac configuration is done by editing the ''''''`trac.ini`'''''' config file, located in `<projectenv>/conf/trac.ini`.  Changes to the configuration are usually reflected immediately, though changes to the `[components]` or `[logging]` sections will require restarting the web server. You may also need to restart the web server after creating a global configuration file when none was previously present.\\r\\n\\r\\nThe `trac.ini` configuration file and its parent directory should be writable by the web server, as Trac currently relies on the possibility to trigger a complete environment reload to flush its caches.\\r\\n\\r\\n== Global Configuration ==\\r\\n\\r\\nIn versions prior to 0.11, the global configuration was by default located in `$prefix/share/trac/conf/trac.ini` or /etc/trac/trac.ini, depending on the distribution. If you''re upgrading, you may want to specify that file to inherit from.  Literally, when you''re upgrading to 0.11, you have to add an `[inherit]` section to your project''s `trac.ini` file. Additionally, you have to move your customized templates and common images from `$prefix/share/trac/...` to the new location.\\r\\n\\r\\nGlobal options will be merged with the environment-specific options, where local options override global options. The options file is specified as follows:\\r\\n{{{\\r\\n[inherit]\\r\\nfile = /path/to/global/trac.ini\\r\\n}}}\\r\\nMultiple files can be specified using a comma-separated list.\\r\\n\\r\\nNote that you can also specify a global option file when creating a new project,  by adding the option `--inherit=/path/to/global/trac.ini` to [TracAdmin#initenv trac-admin]''s `initenv` command.  If you do not do this but nevertheless intend to use a global option file with your new environment, you will have to go through the newly generated `conf/trac.ini` file and delete the entries that will otherwise override those set in the global file.\\r\\n\\r\\nThere are two more entries in the [[#inherit-section| [inherit] ]] section, `templates_dir` for sharing global templates and `plugins_dir`, for sharing plugins. Those entries can themselves be specified in the shared configuration file, and in fact, configuration files can even be chained if you specify another `[inherit] file` there.\\r\\n\\r\\nNote that the templates found in the `templates/` directory of the TracEnvironment have precedence over those found in `[inherit] templates_dir`. In turn, the latter have precedence over the installed templates, so be careful about what you put there, notably if you override a default template be sure to refresh your modifications when you upgrade to a new version of Trac (the preferred way to perform TracInterfaceCustomization being still to write a custom plugin doing an appropriate `ITemplateStreamFilter` transformation).\\r\\n\\r\\n== Reference for settings\\r\\n\\r\\nThis is a brief reference of available configuration options, and their default settings.\\r\\n\\r\\n[[TracIni]]\\r\\n\\r\\n----\\r\\nSee also: TracGuide, TracAdmin, TracEnvironment\\r\\n', NULL, NULL),\n('TracRss', 1, 1362994208315774, 'trac', '127.0.0.1', '= Using RSS with Trac =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nSeveral of the Trac modules support content syndication using the RSS (Really Simple Syndication) XML format.\\r\\nUsing the RSS subscription feature in Trac, you can easily monitor progress of the project, a set of issues or even changes to a single file.\\r\\n\\r\\nTrac supports RSS feeds in:\\r\\n\\r\\n * TracTimeline —  Use the RSS feed to ''''''subscribe to project events''''''.[[br]]Monitor overall project progress in your favorite RSS reader.\\r\\n * TracTickets, TracReports, and TracQuery — Allows syndication of report and ticket query results.[[br]]Be notified about important and relevant issue tickets.\\r\\n * TracBrowser and TracRevisionLog — Syndication of file changes.[[br]]Stay up to date with changes to a specific file or directory.\\r\\n\\r\\n== How to access RSS data ==\\r\\nAnywhere in Trac where RSS is available, you should find a small orange ''''''XML'''''' icon, typically placed at the bottom of the page. Clicking the icon will access the RSS feed for that specific resource.\\r\\n\\r\\n''''''Note:'''''' Different modules provide different data in their RSS feeds. Usually, the syndicated information corresponds to the current view. For example, if you click the RSS link on a report page, the feed will be based on that report. It might be explained by thinking of the RSS feeds as an ''''alternate view of the data currently displayed''''.\\r\\n\\r\\n== Links ==\\r\\n * ''''Specifications:''''\\r\\n   * http://blogs.law.harvard.edu/tech/rss — RSS 2.0 Specification\\r\\n\\r\\n * ''''Multi-platform RSS readers:''''\\r\\n   * http://www.rssowl.org/ — Open source, Eclipse-based, RSS reader for Linux, Mac and Windows systems that supports https and authenticated feeds.\\r\\n\\r\\n * ''''Linux/BSD/*n*x systems:''''\\r\\n   * http://liferea.sourceforge.net/ — Open source GTK2 RSS Reader for Linux\\r\\n   * [http://akregator.sourceforge.net/ Akregator] — Open source KDE RSS Reader (part of KDE-PIM)\\r\\n\\r\\n * ''''Mac OS X systems:''''\\r\\n   * http://ranchero.com/netnewswire/ — An excellent RSS reader for Mac OS X (has both free and pay versions)\\r\\n   * http://www.utsire.com/shrook/ — An RSS reader for Max OS X that supports https (even with self signed certificates) and authenticated feeds.\\r\\n   * http://vienna-rss.sourceforge.net/ — Open source Feed Reader for Mac OS X with smart folders support\\r\\n   * http://www.mesadynamics.com/Tickershock.html — Non-intrusive \"news ticker\" style RSS reader for Mac OS X\\r\\n\\r\\n * ''''Windows systems:''''\\r\\n   * http://www.rssreader.com/ — Free and powerful RSS Reader for Windows\\r\\n   * http://www.sharpreader.net/ — A free RSS Reader written in .NET for Windows\\r\\n\\r\\n * ''''Firefox:''''\\r\\n   * http://www.mozilla.org/products/firefox/ — Mozilla Firefox features plenty [https://addons.mozilla.org/en-US/firefox/search/?q=rss&appver=&platform= add-ons] for supporting RSS\\r\\n\\r\\n----\\r\\nSee also: TracGuide, TracTimeline, TracReports, TracBrowser\\r\\n', NULL, NULL);\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('TracPermissions', 1, 1362994208317356, 'trac', '127.0.0.1', '= Trac Permissions =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nTrac uses a simple, case sensitive, permission system to control what users can and can''t access.\\r\\n\\r\\nPermission privileges are managed using the [TracAdmin trac-admin] tool or (new in version 0.11) the ''''General / Permissions'''' panel in the ''''Admin'''' tab of the web interface.\\r\\n\\r\\nIn addition to the default permission policy described in this page, it is possible to activate additional permission policies by enabling plugins and listing them in the `[trac] permission_policies` configuration entry in the TracIni. See TracFineGrainedPermissions for more details.\\r\\n\\r\\nNon-authenticated users accessing the system are assigned the name \"anonymous\". Assign permissions to the \"anonymous\" user to set privileges for anonymous/guest users. The parts of Trac that a user does not have the privileges for will not be displayed in the navigation.\\r\\nIn addition to these privileges, users can be granted additional individual rights in effect when authenticated and logged into the system. All logged in users belong to the virtual group \"authenticated\", which inherits permissions from \"anonymous\".\\r\\n\\r\\n== Graphical Admin Tab ==\\r\\n''''This feature is new in version 0.11.''''\\r\\n\\r\\nTo access this tab, a user must have one of the following permissions: `TRAC_ADMIN`, `PERMISSION_ADMIN`, `PERMISSION_ADD`, `PERMISSION_REMOVE`. The permissions can granted using the `trac-admin` command (more on `trac-admin` below):\\r\\n{{{\\r\\n  $ trac-admin /path/to/projenv permission add bob TRAC_ADMIN\\r\\n}}}\\r\\n\\r\\nThen, the user `bob` will be able to see the Admin tab, and can then access the permissions menu. This menu will allow you to perform all the following actions, but from the browser without requiring root access to the server (just the correct permissions for your user account). ''''''Use at least one lowercase character in user names, as all-uppercase names are reserved for permissions.''''''\\r\\n\\r\\n 1. [[Image(htdocs:../common/guide/admin.png)]]\\r\\n 1. [[Image(htdocs:../common/guide/admin-permissions.png)]]\\r\\n 1. [[Image(htdocs:../common/guide/admin-permissions-TICKET_ADMIN.png)]]\\r\\n\\r\\nAn easy way to quickly secure a new Trac install is to run the above command on the anonymous user, install the [http://trac-hacks.org/wiki/AccountManagerPlugin AccountManagerPlugin], create a new admin account graphically and then remove the TRAC_ADMIN permission from the anonymous user.\\r\\n\\r\\n== Available Privileges ==\\r\\n\\r\\nTo enable all privileges for a user, use the `TRAC_ADMIN` permission. Having `TRAC_ADMIN` is like being `root` on a *NIX system: it will allow you to perform any operation.\\r\\n\\r\\nOtherwise, individual privileges can be assigned to users for the various different functional areas of Trac (''''''note that the privilege names are case-sensitive''''''):\\r\\n\\r\\n=== Repository Browser ===\\r\\n\\r\\n|| `BROWSER_VIEW` || View directory listings in the [wiki:TracBrowser repository browser] ||\\r\\n|| `LOG_VIEW` || View revision logs of files and directories in the [wiki:TracBrowser repository browser] ||\\r\\n|| `FILE_VIEW` || View files in the [wiki:TracBrowser repository browser] ||\\r\\n|| `CHANGESET_VIEW` || View [wiki:TracChangeset repository check-ins] ||\\r\\n\\r\\n=== Ticket System ===\\r\\n\\r\\n|| `TICKET_VIEW` || View existing [wiki:TracTickets tickets] and perform [wiki:TracQuery ticket queries] ||\\r\\n|| `TICKET_CREATE` || Create new [wiki:TracTickets tickets] ||\\r\\n|| `TICKET_APPEND` || Add comments or attachments to [wiki:TracTickets tickets] ||\\r\\n|| `TICKET_CHGPROP` || Modify [wiki:TracTickets ticket] properties (priority, assignment, keywords, etc.) with the following exceptions: edit description field, add/remove other users from cc field when logged in, and set email to pref ||\\r\\n|| `TICKET_MODIFY` || Includes both `TICKET_APPEND` and `TICKET_CHGPROP`, and in addition allows resolving [wiki:TracTickets tickets]. Tickets can be assigned to users through a [TracTickets#Assign-toasDrop-DownList drop-down list] when the list of possible owners has been restricted. ||\\r\\n|| `TICKET_EDIT_CC` || Full modify cc field ||\\r\\n|| `TICKET_EDIT_DESCRIPTION` || Modify description field ||\\r\\n|| `TICKET_EDIT_COMMENT` || Modify comments ||\\r\\n|| `TICKET_BATCH_MODIFY` || [wiki:TracBatchModify Batch modify] tickets ||\\r\\n|| `TICKET_ADMIN` || All `TICKET_*` permissions, plus the deletion of ticket attachments and modification of the reporter and description fields. It also allows managing ticket properties in the WebAdmin panel. ||\\r\\n\\r\\nAttention: the \"view tickets\" button appears with the `REPORT_VIEW` permission.\\r\\n\\r\\n=== Roadmap ===\\r\\n\\r\\n|| `MILESTONE_VIEW` || View milestones and assign tickets to milestones. ||\\r\\n|| `MILESTONE_CREATE` || Create a new milestone ||\\r\\n|| `MILESTONE_MODIFY` || Modify existing milestones ||\\r\\n|| `MILESTONE_DELETE` || Delete milestones ||\\r\\n|| `MILESTONE_ADMIN` || All `MILESTONE_*` permissions ||\\r\\n|| `ROADMAP_VIEW` || View the [wiki:TracRoadmap roadmap] page, is not (yet) the same as MILESTONE_VIEW, see [trac:#4292 #4292] ||\\r\\n|| `ROADMAP_ADMIN` || to be removed with [trac:#3022 #3022], replaced by MILESTONE_ADMIN ||\\r\\n\\r\\n=== Reports ===\\r\\n\\r\\n|| `REPORT_VIEW` || View [wiki:TracReports reports], i.e. the \"view tickets\" link. ||\\r\\n|| `REPORT_SQL_VIEW` || View the underlying SQL query of a [wiki:TracReports report] ||\\r\\n|| `REPORT_CREATE` || Create new [wiki:TracReports reports] ||\\r\\n|| `REPORT_MODIFY` || Modify existing [wiki:TracReports reports] ||\\r\\n|| `REPORT_DELETE` || Delete [wiki:TracReports reports] ||\\r\\n|| `REPORT_ADMIN` || All `REPORT_*` permissions ||\\r\\n\\r\\n=== Wiki System ===\\r\\n\\r\\n|| `WIKI_VIEW` || View existing [wiki:TracWiki wiki] pages ||\\r\\n|| `WIKI_CREATE` || Create new [wiki:TracWiki wiki] pages ||\\r\\n|| `WIKI_MODIFY` || Change [wiki:TracWiki wiki] pages ||\\r\\n|| `WIKI_RENAME` || Rename [wiki:TracWiki wiki] pages ||\\r\\n|| `WIKI_DELETE` || Delete [wiki:TracWiki wiki] pages and attachments ||\\r\\n|| `WIKI_ADMIN` || All `WIKI_*` permissions, plus the management of ''''readonly'''' pages. ||\\r\\n\\r\\n=== Permissions ===\\r\\n\\r\\n|| `PERMISSION_GRANT` || add/grant a permission ||\\r\\n|| `PERMISSION_REVOKE` || remove/revoke a permission ||\\r\\n|| `PERMISSION_ADMIN` || All `PERMISSION_*` permissions ||\\r\\n\\r\\n=== Others ===\\r\\n\\r\\n|| `TIMELINE_VIEW` || View the [wiki:TracTimeline timeline] page ||\\r\\n|| `SEARCH_VIEW` || View and execute [wiki:TracSearch search] queries ||\\r\\n|| `CONFIG_VIEW` || Enables additional pages on ''''About Trac'''' that show the current configuration or the list of installed plugins ||\\r\\n|| `EMAIL_VIEW` || Shows email addresses even if [wiki:TracIni#trac-section trac show_email_addresses] configuration option is false ||\\r\\n\\r\\n== Creating New Privileges ==\\r\\n\\r\\nTo create custom permissions, for example to be used in a custom workflow, enable the optional [trac:ExtraPermissionsProvider tracopt.perm.config_perm_provider.ExtraPermissionsProvider] component in the \"Plugins\" admin panel, and add the desired permissions to the `[extra-permissions]` section in your [wiki:TracIni#extra-permissions-section trac.ini]. For more information, please refer to the documentation of the component in the admin panel.\\r\\n\\r\\n== Granting Privileges ==\\r\\n\\r\\nYou grant privileges to users using [wiki:TracAdmin trac-admin]. The current set of privileges can be listed with the following command:\\r\\n{{{\\r\\n  $ trac-admin /path/to/projenv permission list\\r\\n}}}\\r\\n\\r\\nThis command will allow the user ''''bob'''' to delete reports:\\r\\n{{{\\r\\n  $ trac-admin /path/to/projenv permission add bob REPORT_DELETE\\r\\n}}}\\r\\n\\r\\nThe `permission add` command also accepts multiple privilege names:\\r\\n{{{\\r\\n  $ trac-admin /path/to/projenv permission add bob REPORT_DELETE WIKI_CREATE\\r\\n}}}\\r\\n\\r\\nOr add all privileges:\\r\\n{{{\\r\\n  $ trac-admin /path/to/projenv permission add bob TRAC_ADMIN\\r\\n}}}\\r\\n\\r\\n== Permission Groups ==\\r\\n\\r\\nThere are two built-in groups, \"authenticated\" and \"anonymous\".\\r\\nAny user who has not logged in is automatically in the \"anonymous\" group.\\r\\nAny user who has logged in is also in the \"authenticated\" group.\\r\\nThe \"authenticated\" group inherits permissions from the \"anonymous\" group.\\r\\nFor example, if the \"anonymous\" group has permission WIKI_MODIFY, \\r\\nit is not necessary to add the WIKI_MODIFY permission to the \"authenticated\" group as well.\\r\\n\\r\\nCustom groups may be defined that inherit permissions from the two built-in groups.\\r\\n\\r\\nPermissions can be grouped together to form roles such as ''''developer'''', ''''admin'''', etc.\\r\\n{{{\\r\\n  $ trac-admin /path/to/projenv permission add developer WIKI_ADMIN\\r\\n  $ trac-admin /path/to/projenv permission add developer REPORT_ADMIN\\r\\n  $ trac-admin /path/to/projenv permission add developer TICKET_MODIFY\\r\\n  $ trac-admin /path/to/projenv permission add bob developer\\r\\n  $ trac-admin /path/to/projenv permission add john developer\\r\\n}}}\\r\\n\\r\\nGroup membership can be checked by doing a {{{permission list}}} with no further arguments; the resulting output will include group memberships. ''''''Use at least one lowercase character in group names, as all-uppercase names are reserved for permissions''''''.\\r\\n\\r\\n== Adding a New Group and Permissions ==\\r\\nPermission groups can be created by assigning a user to a group you wish to create, then assign permissions to that group.\\r\\n\\r\\nThe following will add ''''bob'''' to the new group called ''''beta_testers'''' and then will assign WIKI_ADMIN permissions to that group. (Thus, ''''bob'''' will inherit the WIKI_ADMIN permission)\\r\\n{{{ \\r\\n   $ trac-admin /path/to/projenv permission add bob beta_testers\\r\\n   $ trac-admin /path/to/projenv permission add beta_testers WIKI_ADMIN\\r\\n\\r\\n}}}\\r\\n\\r\\n== Removing Permissions ==\\r\\n\\r\\nPermissions can be removed using the ''remove'' command. For example:\\r\\n\\r\\nThis command will prevent the user ''''bob'''' from deleting reports:\\r\\n{{{\\r\\n  $ trac-admin /path/to/projenv permission remove bob REPORT_DELETE\\r\\n}}}\\r\\n\\r\\nJust like `permission add`, this command accepts multiple privilege names.\\r\\n\\r\\nYou can also remove all privileges for a specific user:\\r\\n{{{\\r\\n  $ trac-admin /path/to/projenv permission remove bob ''*''\\r\\n}}}\\r\\n\\r\\nOr one privilege for all users:\\r\\n{{{\\r\\n  $ trac-admin /path/to/projenv permission remove ''*'' REPORT_ADMIN\\r\\n}}}\\r\\n\\r\\n== Default Permissions ==\\r\\n\\r\\nBy default on a new Trac installation, the `anonymous` user will have ''''view'''' access to everything in Trac, but will not be able to create or modify anything.\\r\\nOn the other hand, the `authenticated` users will have the permissions to ''''create and modify tickets and wiki pages''''.\\r\\n\\r\\n''''''anonymous''''''\\r\\n{{{\\r\\n BROWSER_VIEW \\r\\n CHANGESET_VIEW \\r\\n FILE_VIEW \\r\\n LOG_VIEW \\r\\n MILESTONE_VIEW \\r\\n REPORT_SQL_VIEW \\r\\n REPORT_VIEW \\r\\n ROADMAP_VIEW \\r\\n SEARCH_VIEW \\r\\n TICKET_VIEW \\r\\n TIMELINE_VIEW\\r\\n WIKI_VIEW\\r\\n}}}\\r\\n\\r\\n''''''authenticated''''''\\r\\n{{{\\r\\n TICKET_CREATE \\r\\n TICKET_MODIFY \\r\\n WIKI_CREATE \\r\\n WIKI_MODIFY  \\r\\n}}}\\r\\n----\\r\\nSee also: TracAdmin, TracGuide and TracFineGrainedPermissions\\r\\n', NULL, NULL),\n('RecentChanges', 1, 1362994208319707, 'trac', '127.0.0.1', ''''''' [TitleIndex Index by Title] '''''' | '''''' Index by Date ''''''\\r\\n\\r\\n[[RecentChanges]]', NULL, NULL),\n('TracBackup', 1, 1362994208320928, 'trac', '127.0.0.1', '= Trac Backup =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nSince Trac uses a database backend, some extra care is required to safely create a backup of a [wiki:TracEnvironment project environment]. Luckily, [wiki:TracAdmin trac-admin] has a command to make backups easier: `hotcopy`.\\r\\n\\r\\n  ''''Note: Trac uses the `hotcopy` nomenclature to match that of [http://subversion.tigris.org/ Subversion], to make it easier to remember when managing both Trac and Subversion servers.''''\\r\\n\\r\\n== Creating a Backup ==\\r\\n\\r\\nTo create a backup of a live TracEnvironment, simply run:\\r\\n{{{\\r\\n\\r\\n  $ trac-admin /path/to/projenv hotcopy /path/to/backupdir\\r\\n\\r\\n}}}\\r\\n\\r\\n[wiki:TracAdmin trac-admin] will lock the database while copying.''''\\r\\n\\r\\nThe resulting backup directory is safe to handle using standard file-based backup tools like `tar` or `dump`/`restore`.\\r\\n\\r\\nPlease, note, that hotcopy command does not overwrite target directory and when such exists, hotcopy ends with error: `Command failed: [Errno 17] File exists:` This is discussed in [trac:ticket:3198 #3198].\\r\\n\\r\\n=== Restoring a Backup ===\\r\\n\\r\\nBackups are simply a copied snapshot of the entire [wiki:TracEnvironment project environment] directory, including the SQLite database. \\r\\n\\r\\nTo restore an environment from a backup, stop the process running Trac (i.e. the Web server or [wiki:TracStandalone tracd]), restore the contents of your backup (path/to/backupdir) to your [wiki:TracEnvironment project environment] directory and restart the service.\\r\\n\\r\\n----\\r\\nSee also: TracAdmin, TracEnvironment, TracGuide, [trac:TracMigrate TracMigrate]\\r\\n', NULL, NULL),\n('TracSyntaxColoring', 1, 1362994208322350, 'trac', '127.0.0.1', '= Syntax Coloring of Source Code =\\r\\nTrac supports language-specific syntax highlighting of source code within wiki formatted text in [WikiProcessors#CodeHighlightingSupport wiki processors] blocks and in the [TracBrowser repository browser].\\r\\n\\r\\nTo do this, Trac uses external libraries with support for a great number of programming languages.\\r\\n\\r\\nCurrently Trac supports syntax coloring using one or more of the following packages:\\r\\n\\r\\n * [http://pygments.pocoo.org/ Pygments], by far the preferred system, as it covers a wide range of programming languages and other structured texts and is actively supported\\r\\n * [http://www.codento.com/people/mtr/genscript/ GNU Enscript], commonly available on Unix but somewhat unsupported on Windows\\r\\n * [http://silvercity.sourceforge.net/ SilverCity], legacy system, some versions can be [http://trac.edgewall.org/wiki/TracFaq#why-is-my-css-code-not-being-highlighted-even-though-i-have-silvercity-installed problematic]\\r\\n\\r\\n\\r\\nTo activate syntax coloring, simply install either one (or more) of these packages (see [#ExtraSoftware] section below).\\r\\nIf none of these packages is available, Trac will display the data as plain text. \\r\\n\\r\\n\\r\\n=== About Pygments ===\\r\\n\\r\\nStarting with trac 0.11 [http://pygments.org/ pygments] will be the new default highlighter. It''s a highlighting library implemented in pure python, very fast, easy to extend and [http://pygments.org/docs/ well documented].\\r\\n\\r\\nThe Pygments default style can specified in the [TracIni#mimeviewer-section mime-viewer] section of trac.ini. The default style can be overridden by setting a Style preference on the [/prefs/pygments preferences page]. \\r\\n\\r\\nIt''s very likely that the list below is outdated because the list of supported pygments lexers is growing weekly. Just have a look at the page of [http://pygments.org/docs/lexers/ supported lexers] on the pygments webpage.\\r\\n\\r\\n\\r\\n== Syntax Coloring Support ==\\r\\n\\r\\n=== Known MIME Types\\r\\n\\r\\n[[KnownMimeTypes]]\\r\\n\\r\\nNote that the rich content may be directly //rendered// instead of syntax highlighted. This usually depends on which auxiliary packages are installed and on which components are activated in your setup. For example a `text/x-rst` document will be rendered via `docutils` if it is installed and the `trac.mimeview.rst.ReStructuredTextRenderer` is not disabled, and will be syntax highlighted otherwise.\\r\\n\\r\\nIn a similar way, a document with the mimetype `text/x-trac-wiki` is rendered using the Trac wiki formatter, unless the `trac.mimeview.api.WikiTextRenderer` component is disabled.\\r\\n\\r\\nHTML documents are directly rendered only if the `render_unsafe_html` settings are enabled in the TracIni (those settings are present in multiple sections, as there are different security concerns depending where the document comes from). If you want to ensure that an HTML document gets syntax highlighted and not rendered, use the `text/xml` mimetype.\\r\\n\\r\\nIf mimetype such as ''svn:mime-type'' is set to ''text/plain'', there is no coloring even if file is known type like ''java''.\\r\\n\\r\\n=== List of Languages Supported, by Highlighter #language-supported\\r\\n\\r\\nThis list is only indicative.\\r\\n\\r\\n||                 ||= !SilverCity   =||= Enscript      =||= Pygments =||\\r\\n|| Ada             ||                 ||  ✓              ||     ||\\r\\n|| Asm             ||                 ||  ✓              ||     ||\\r\\n|| Apache Conf     ||                 ||                 ||  ✓  ||\\r\\n|| ASP             ||  ✓              ||  ✓              ||     ||\\r\\n|| C               ||  ✓              ||  ✓              ||  ✓  ||\\r\\n|| C#              ||                 ||  ✓ ^[#a1 (1)]^  ||  ✓  ||\\r\\n|| C++             ||  ✓              ||  ✓              ||  ✓  ||\\r\\n|| CMake           ||  ?              ||  ?              ||  ✓  ||\\r\\n|| Java            ||  ✓ ^[#a2 (2)]^  ||  ✓              ||  ✓  ||\\r\\n|| Awk             ||                 ||  ✓              ||     ||\\r\\n|| Boo             ||                 ||                 ||  ✓  ||\\r\\n|| CSS             ||  ✓              ||                 ||  ✓  ||\\r\\n|| Python Doctests ||                 ||                 ||  ✓  ||\\r\\n|| Diff            ||                 ||  ✓              ||  ✓  ||\\r\\n|| Eiffel          ||                 ||  ✓              ||     ||\\r\\n|| Elisp           ||                 ||  ✓              ||     ||\\r\\n|| Fortran         ||                 ||  ✓ ^[#a1 (1)]^  ||  ✓  ||\\r\\n|| Haskell         ||                 ||  ✓              ||  ✓  ||\\r\\n|| Genshi          ||                 ||                 ||  ✓  ||\\r\\n|| HTML            ||  ✓              ||  ✓              ||  ✓  ||\\r\\n|| IDL             ||                 ||  ✓              ||     ||\\r\\n|| INI             ||                 ||                 ||  ✓  ||\\r\\n|| Javascript      ||  ✓              ||  ✓              ||  ✓  ||\\r\\n|| Lua             ||                 ||                 ||  ✓  ||\\r\\n|| m4              ||                 ||  ✓              ||     ||\\r\\n|| Makefile        ||                 ||  ✓              ||  ✓  ||\\r\\n|| Mako            ||                 ||                 ||  ✓  ||\\r\\n|| Matlab ^[#a3 (3)]^  ||             ||  ✓              ||  ✓  ||\\r\\n|| Mygthy          ||                 ||                 ||  ✓  ||\\r\\n|| Objective-C     ||                 ||  ✓              ||  ✓  ||\\r\\n|| OCaml           ||                 ||                 ||  ✓  ||\\r\\n|| Pascal          ||                 ||  ✓              ||  ✓  ||\\r\\n|| Perl            ||  ✓              ||  ✓              ||  ✓  ||\\r\\n|| PHP             ||  ✓              ||                 ||  ✓  ||\\r\\n|| PSP             ||  ✓              ||                 ||     ||\\r\\n|| Pyrex           ||                 ||  ✓              ||     ||\\r\\n|| Python          ||  ✓              ||  ✓              ||  ✓  ||\\r\\n|| Ruby            ||  ✓              ||  ✓ ^[#a1 (1)]^  ||  ✓  ||\\r\\n|| Scheme          ||                 ||  ✓              ||  ✓  ||\\r\\n|| Shell           ||                 ||  ✓              ||  ✓  ||\\r\\n|| Smarty          ||                 ||                 ||  ✓  ||\\r\\n|| SQL             ||  ✓              ||  ✓              ||  ✓  ||\\r\\n|| Troff           ||                 ||  ✓              ||  ✓  ||\\r\\n|| TCL             ||                 ||  ✓              ||     ||\\r\\n|| Tex             ||                 ||  ✓              ||  ✓  ||\\r\\n|| Verilog         ||  ✓ ^[#a2 (2)]^  ||  ✓              ||     ||\\r\\n|| VHDL            ||                 ||  ✓              ||     ||\\r\\n|| Visual Basic    ||                 ||  ✓              ||  ✓  ||\\r\\n|| VRML            ||                 ||  ✓              ||     ||\\r\\n|| XML             ||  ✓              ||                 ||  ✓  ||\\r\\n\\r\\n\\r\\n\\r\\n''''[=#a1 (1)] Not included in the Enscript distribution.  Additional highlighting rules can be obtained for\\r\\n[http://neugierig.org/software/ruby/ Ruby],\\r\\n[http://wiki.hasno.info/index.php/Csharp.st C#],\\r\\n[http://wiki.hasno.info/index.php/F90.st Fortran 90x/2003]\\r\\n\\r\\n''''[=#a2 (2)] since Silvercity 0.9.7 released on 2006-11-23\\r\\n\\r\\n''''[=#a3 (3)] By default `.m` files are considered Objective-C files. In order to treat `.m` files as MATLAB files, add \"text/matlab:m\" to the \"mime_map\" setting in the [wiki:TracIni#mimeviewer-section \"[mimeviewer] section of trac.ini\"].\\r\\n\\r\\n== Extra Software ==\\r\\n * GNU Enscript — http://directory.fsf.org/GNU/enscript.html\\r\\n * GNU Enscript for Windows — http://gnuwin32.sourceforge.net/packages/enscript.htm\\r\\n * !SilverCity — http://silvercity.sf.net/\\r\\n * **Pygments — http://pygments.org/**\\r\\n\\r\\n----\\r\\nSee also: WikiProcessors, WikiFormatting, TracWiki, TracBrowser\\r\\n', NULL, NULL),\n('TracChangeset', 1, 1362994208324007, 'trac', '127.0.0.1', '= Trac Changeset Module =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nTrac has a built-in functionality for visualizing “diffs” - changes to files.\\r\\n\\r\\nThere are different kinds of ''''change sets''''. \\r\\nSome can correspond to revisions made in the repositories,\\r\\nothers can aggregate changes made in several revisions, \\r\\nbut in the end, any kind of differences can be shown.\\r\\n\\r\\nThe changeset view consists of two parts, the ''''header'''' \\r\\nand the ''''diff views''''.\\r\\n\\r\\n== Changeset Header ==\\r\\n\\r\\nThe header shows an overview of the whole changeset.\\r\\nHere you will find information such as:\\r\\n\\r\\n * Timestamp — When the changeset was commited\\r\\n * Author — Who commited the changeset\\r\\n * Message — A brief description from the author (the commit log message)\\r\\n * Location — Parent directory of all files affected by this changeset\\r\\n * Files — A list of files affected by this changeset\\r\\n\\r\\nIf more than one revision is involved in the set of changes being\\r\\ndisplayed, the ''''Timestamp'''', ''''Author'''' and ''''Message'''' fields \\r\\nwon''t be shown.\\r\\n\\r\\nIn front of each listed file, you''ll find  a colored rectangle. The color\\r\\nindicates how the file is affected by the changeset.\\r\\n \\r\\n [[span(style=background:#bfb;border:1px solid #999;font-size:80%;margin-right:.5em,''''  '''')]] Green: Added \\\\\\\\\\r\\n [[span(style=background:#f88;border:1px solid #999;font-size:80%;margin-right:.5em,''''  '''')]] Red: Removed \\\\\\\\\\r\\n [[span(style=background:#fd8;border:1px solid #999;font-size:80%;margin-right:.5em,''''  '''')]] Yellow: Modified \\\\\\\\\\r\\n [[span(style=background:#88f;border:1px solid #999;font-size:80%;margin-right:.5em,''''  '''')]] Blue: Copied \\\\\\\\\\r\\n [[span(style=background:#ccc;border:1px solid #999;font-size:80%;margin-right:.5em,''''  '''')]] Gray: Moved \\\\\\\\\\r\\nThe color legend is located below the header as a reminder.\\r\\n\\r\\n== Diff Views ==\\r\\n\\r\\nBelow the header is the main part of the changeset, the diff view. Each file is shown in a separate section, each of which will contain only the regions of the file that are affected by the changeset. There are two different styles of displaying the diffs: ''''inline'''' or ''''side-by-side'''' (you can switch between those styles using the preferences form):\\r\\n\\r\\n * The ''''inline'''' style shows the changed regions of a file underneath each other. A region removed from the file will be colored red, an added region will be colored green. If a region was modified, the old version is displayed above the new version. Line numbers on the left side indicate the exact position of the change in both the old and the new version of the file.\\r\\n * The ''''side-by-side'''' style shows the old version on the left and the new version on the right (this will typically require more screen width than the inline style.) Added and removed regions will be colored in the same way as with the inline style (green and red, respectively), but modified regions will have a yellow background.\\r\\n\\r\\nIn addition, various advanced options are available in the preferences form for adjusting the display of the diffs:\\r\\n * You can set how many lines are displayed before and after every change\\r\\n   (if the value ''''all'''' is used, then the full file will be shown)\\r\\n * You can toggle whether blank lines, case changes and white space changes are ignored, thereby letting you find the functional changes more quickly\\r\\n\\r\\n\\r\\n== The Different Ways to Get a Diff ==\\r\\n\\r\\n=== Examining a Changeset ===\\r\\n\\r\\nWhen viewing a repository check-in, such as when following a\\r\\nchangeset [wiki:TracLinks link] or a changeset event in the \\r\\n[wiki:TracTimeline timeline], Trac will display the exact changes\\r\\nmade by the check-in.\\r\\n\\r\\nThere will be also navigation links to the ''''Previous Changeset''''\\r\\nto and ''''Next Changeset''''.\\r\\n\\r\\n=== Examining Differences Between Revisions ===\\r\\n\\r\\nOften you''ll want to look at changes made on a file \\r\\nor on a directory spanning multiple revisions. The easiest way\\r\\nto get there is from the TracRevisionLog, where you can select\\r\\nthe ''''old'''' and the ''''new'''' revisions of the file or directory, and\\r\\nthen click the ''''View changes'''' button.\\r\\n\\r\\n=== Examining Differences Between Branches ===\\r\\n\\r\\nOne of the core features of version control systems is the possibility\\r\\nto work simultaneously on different ''''Lines of Developments'''', commonly\\r\\ncalled “branches”. Trac enables you to examine the exact differences\\r\\nbetween such branches.\\r\\n\\r\\nUsing the ''''''View changes ...'''''' button in the TracBrowser allows you to enter\\r\\n''''From:'''' and ''''To:'''' path/revision pairs. The resulting set of differences consist\\r\\nof the changes that should be applied to the ''''From:'''' content in order\\r\\nto get to the ''''To:'''' content.\\r\\n\\r\\nFor convenience, it is possible to invert the roles of the ''''old'''' and the ''''new''''\\r\\npath/revision pairs by clicking the ''''Reverse Diff'''' link on the changeset page.\\r\\n\\r\\n=== Checking the Last Change ===\\r\\n\\r\\nThe last possibility for examining changes is to use the ''''Last Change''''\\r\\nlink provided by the TracBrowser.\\r\\n\\r\\nThis link will take you to the last change that was made on that path.\\r\\nFrom there, you can use the ''''Previous Change'''' and ''''Next Change'''' links\\r\\nto traverse the change history of the file or directory.\\r\\n\\r\\n----\\r\\nSee also: TracGuide, TracBrowser\\r\\n', NULL, NULL),\n('WikiRestructuredText', 1, 1362994208325755, 'trac', '127.0.0.1', '= reStructuredText Support in Trac =\\r\\n\\r\\nTrac supports using ''''reStructuredText'''' (RST) as an alternative to wiki markup in any context WikiFormatting is used.\\r\\n\\r\\nFrom the reStucturedText webpage:\\r\\n \"''''reStructuredText is an easy-to-read, what-you-see-is-what-you-get plaintext markup syntax and parser   system. It is useful for in-line program documentation (such as Python docstrings), for quickly creating  simple web pages, and for standalone documents. reStructuredText is designed for extensibility for  specific application domains. ''''\"\\r\\n\\r\\nIf you want a file from your Subversion repository be displayed as reStructuredText in Trac''s source browser, set `text/x-rst` as value for the Subversion property `svn:mime-type`. See [trac:source:/trunk/INSTALL this example].\\r\\n\\r\\n=== Requirements ===\\r\\nNote that to activate RST support in Trac, the python docutils package must be installed. \\r\\nIf not already available on your operating system, you can download it at the [http://docutils.sourceforge.net/rst.html RST Website].\\r\\n\\r\\nInstall docutils using `easy_install docutils`. Do not use the package manager of your OS (e.g. `apt-get install python-docutils`), because Trac will not find docutils then.\\r\\n\\r\\n=== More information on RST ===\\r\\n\\r\\n * reStructuredText Website -- http://docutils.sourceforge.net/rst.html\\r\\n * RST Quick Reference -- http://docutils.sourceforge.net/docs/rst/quickref.html\\r\\n\\r\\n----\\r\\n\\r\\n== Using RST in Trac ==\\r\\nTo specify that a block of text should be parsed using RST, use the ''''rst'''' processor. \\r\\n\\r\\n=== TracLinks in reStructuredText ===\\r\\n\\r\\n * Trac provides a custom RST directive `trac::` to allow TracLinks from within RST text.\\r\\n   ||= Wiki Markup ||= Display ||\\r\\n   {{{#!td\\r\\n     {{{\\r\\n     {{{\\r\\n     #!rst\\r\\n     This is a reference to |a ticket|\\r\\n\\r\\n     .. |a ticket| trac:: #42\\r\\n     }}}\\r\\n     }}}\\r\\n   }}}\\r\\n   {{{#!td\\r\\n     {{{\\r\\n     #!rst\\r\\n     This is a reference to |a ticket|\\r\\n\\r\\n     .. |a ticket| trac:: #42\\r\\n     }}}\\r\\n   }}}\\r\\n\\r\\n * Trac allows an even easier way of creating TracLinks in RST, using the custom `:trac:` role.\\r\\n   ||= Wiki Markup ||= Display ||\\r\\n   {{{#!td\\r\\n     {{{\\r\\n     {{{\\r\\n     #!rst\\r\\n     This is a reference to ticket `#12`:trac:\\r\\n\\r\\n     To learn how to use Trac, see `TracGuide`:trac:\\r\\n     }}}\\r\\n     }}}\\r\\n   }}}\\r\\n   {{{#!td\\r\\n     {{{\\r\\n     #!rst\\r\\n     This is a reference to ticket `#12`:trac:\\r\\n\\r\\n     To learn how to use Trac, see `TracGuide`:trac:\\r\\n     }}}\\r\\n   }}}\\r\\n\\r\\n For a complete example of all uses of the `:trac:` role, please see WikiRestructuredTextLinks. \\r\\n\\r\\n\\r\\n=== Syntax highlighting in reStructuredText ===\\r\\n\\r\\nThere is a directive for doing TracSyntaxColoring in RST as well. The directive is called\\r\\ncode-block\\r\\n\\r\\n||= Wiki Markup ||= Display ||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  {{{\\r\\n  #!rst\\r\\n\\r\\n  .. code-block:: python\\r\\n\\r\\n     class Test:\\r\\n\\r\\n         def TestFunction(self):\\r\\n             pass\\r\\n  \\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n  {{{\\r\\n  #!rst\\r\\n\\r\\n  .. code-block:: python\\r\\n\\r\\n     class Test:\\r\\n\\r\\n         def TestFunction(self):\\r\\n             pass\\r\\n\\r\\n  }}}\\r\\n}}}\\r\\nNote the need to indent the code at least one character after the `.. code-block` directive.\\r\\n\\r\\n=== Wiki Macros in reStructuredText ===\\r\\n\\r\\nFor doing [WikiMacros Wiki Macros] in RST you use the same directive as for syntax highlighting i.e code-block.\\r\\n\\r\\n||= Wiki Markup ||= Display ||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  {{{\\r\\n  #!rst\\r\\n\\r\\n  .. code-block:: RecentChanges\\r\\n\\r\\n     Trac,3\\r\\n\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n  {{{\\r\\n  #!rst\\r\\n\\r\\n  .. code-block:: RecentChanges\\r\\n\\r\\n     Trac,3\\r\\n\\r\\n  }}}\\r\\n}}}\\r\\n\\r\\nOr a more concise Wiki Macro like syntax is also available, using the `:code-block:` role:\\r\\n\\r\\n||= Wiki Markup ||= Display ||\\r\\n{{{#!td\\r\\n  {{{\\r\\n  {{{\\r\\n  #!rst\\r\\n\\r\\n  :code-block:`RecentChanges:Trac,3`\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n  {{{\\r\\n  #!rst\\r\\n\\r\\n  :code-block:`RecentChanges:Trac,3`\\r\\n  }}}\\r\\n}}}\\r\\n\\r\\n=== Bigger RST Example ===\\r\\nThe example below should be mostly self-explanatory:\\r\\n\\r\\n||= Wiki Markup ||= Display ||\\r\\n{{{#!td\\r\\n{{{\\r\\n#!html\\r\\n<pre class=\"wiki\">{{{\\r\\n#!rst\\r\\nFooBar Header\\r\\n=============\\r\\nreStructuredText is **nice**. It has its own webpage_.\\r\\n\\r\\nA table:\\r\\n\\r\\n=====  =====  ======\\r\\n   Inputs     Output\\r\\n------------  ------\\r\\n  A      B    A or B\\r\\n=====  =====  ======\\r\\nFalse  False  False\\r\\nTrue   False  True\\r\\nFalse  True   True\\r\\nTrue   True   True\\r\\n=====  =====  ======\\r\\n\\r\\nRST TracLinks\\r\\n-------------\\r\\n\\r\\nSee also ticket `#42`:trac:.\\r\\n\\r\\n.. _webpage: http://docutils.sourceforge.net/rst.html\\r\\n}}}</pre>\\r\\n}}}\\r\\n}}}\\r\\n{{{#!td\\r\\n{{{\\r\\n#!rst\\r\\nFooBar Header\\r\\n=============\\r\\nreStructuredText is **nice**. It has its own webpage_.\\r\\n\\r\\nA table:\\r\\n\\r\\n=====  =====  ======\\r\\n   Inputs     Output\\r\\n------------  ------\\r\\n  A      B    A or B\\r\\n=====  =====  ======\\r\\nFalse  False  False\\r\\nTrue   False  True\\r\\nFalse  True   True\\r\\nTrue   True   True\\r\\n=====  =====  ======\\r\\n\\r\\nRST TracLinks\\r\\n-------------\\r\\n\\r\\nSee also ticket `#42`:trac:.\\r\\n\\r\\n.. _webpage: http://docutils.sourceforge.net/rst.html\\r\\n}}}\\r\\n}}}\\r\\n\\r\\n----\\r\\nSee also: WikiRestructuredTextLinks, WikiProcessors, WikiFormatting\\r\\n', NULL, NULL),\n('TracAccessibility', 1, 1362994208328046, 'trac', '127.0.0.1', '= Accessibility Support in Trac =\\r\\n\\r\\nNot every user has a graphic environment with a mouse or other pointing device. Some users rely on keyboard, alternative keyboard or voice input to navigate links, activate form controls, etc. In Trac, we work to assure users may interact with devices other than a pointing device.\\r\\n\\r\\nThe keyboard shortcuts must be enabled for a session through the [/prefs/keybindings Keyboard Shortcuts] preferences panel.\\r\\n\\r\\nTrac supports accessibility keys for the most common operations.\\r\\n - on Linux platforms, press any of the keys listed below in combination with the `<Alt>` key \\r\\n - on a Mac, use the `<ctrl>` key instead\\r\\n - on Windows, you need to hit `<Shift> + <Alt> + <Key>`. This works for most browsers (Firefox, Chrome, Safari and Internet Explorer)\\r\\n\\r\\n== Global Access Keys ==\\r\\n\\r\\n * `1` - WikiStart\\r\\n * `2` - [wiki:TracTimeline Timeline]\\r\\n * `3` - [wiki:TracRoadmap Roadmap]\\r\\n * `4` - [wiki:TracSearch Search]\\r\\n * `6` - [wiki:TracGuide Trac Guide / Documentation]\\r\\n * `7` - [wiki:TracTickets New Ticket]\\r\\n * `9` - [/about About Trac]\\r\\n * `0` - This page\\r\\n * `e` - Edit this page\\r\\n * `f` - Search\\r\\n\\r\\n\\r\\n----\\r\\nSee also: TracGuide', NULL, NULL),\n('TracUnicode', 1, 1362994208329584, 'trac', '127.0.0.1', '= Unicode Support in Trac =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nTrac stores all text using UTF-8 encoding, including text in tickets and wiki pages. Internal processing of text uses true Unicode representations.\\r\\n\\r\\nAs such, it supports most (all?) commonly used character encodings.\\r\\n\\r\\nIf the default encoding in your source code repository is not UTF-8, you can specify it in the [TracIni#trac-section trac.ini], for example:\\r\\n{{{\\r\\ndefault_charset = gbk\\r\\n}}}\\r\\n\\r\\nYou also must make sure that your [trac:DatabaseBackend database backend] stores its data in UTF-8; otherwise strange things will happen.\\r\\n\\r\\nTo convert your database to UTF-8, the easiest way is to dump the database, convert the dump into UTF-8 and then import the converted dump back into the database.[[BR]]\\r\\nYou can use [http://www.gnu.org/software/libiconv/documentation/libiconv/iconv.1.html iconv] to convert the dump.\\r\\n\\r\\n\\r\\n== Examples ==\\r\\n\\r\\n=== Arabic ===\\r\\nتراك يقوم بحفظ كل الكلمات باستخدام صيغة UTF-8، بما في ذلك الكلمات المستخدمة في صفحات  التيكت والويكي.\\r\\n\\r\\n=== Bulgarian ===\\r\\nБългарският език работи ли?\\r\\n\\r\\n=== Česky ===\\r\\nČeština v kódování UTF-8, žádný problém.\\r\\n\\r\\n=== Chinese ===\\r\\nTraditional: 繁體中文, 漢字測試; Simplified: 简体中文，汉字测试\\r\\n\\r\\n=== Croatian ===\\r\\nAko podržava srpski i slovenski mora podržavati i Hrvatski - čćžšđ ČĆŽŠĐ \\r\\n\\r\\n=== English ===\\r\\nYes indeed, Trac supports English. Fully.\\r\\n\\r\\n=== Français ===\\r\\nIl est possible d''écrire en Français : à, ç, û, ...\\r\\n\\r\\n=== German ===\\r\\nTrac-Wiki muß auch deutsche Umlaute richtig anzeigen: ö, ä, ü, Ä, Ö, Ü; und das scharfe ß\\r\\n\\r\\n=== Greek ===\\r\\nΤα Ελληνικά υποστηρίζονται επαρκώς επίσης.\\r\\n\\r\\n=== Hebrew ===\\r\\nאני יכול לאכול זכוכית וזה לא מזיק לי\\r\\n\\r\\n=== Hindi ===\\r\\nअब हिन्दी में।\\r\\n\\r\\n=== Hungarian ===\\r\\nÁrvíztűrő tükörfúrógép\\r\\n\\r\\n=== Icelandic ===\\r\\nÆvar sagði við ömmu sína: Sjáðu hvað ég er stór!\\r\\n\\r\\n=== Japanese ===\\r\\n漢字 ひらがな カタカナ ﾊﾝｶｸｶﾅ 日本語試験\\r\\n\\r\\n=== Korean ===\\r\\n이번에는 한글로 써보겠습니다. 잘 보이나요? 한글\\r\\n\\r\\n=== Latvian ===\\r\\n\\r\\nLatviešu valoda arī strādā!\\r\\n\\r\\n=== Lithuanian ===\\r\\nSudalyvaukime ir mes. Ar veikia lietuviškos raidės? ąčęėįšųūž ĄČĘĖĮŠŲŪŽ Žinoma, kad veikia :)\\r\\nKas tie mes?\\r\\n\\r\\n=== Persian (Farsi) ===\\r\\nاین یک متن فارسی است ولی امکان نوشتن مستقیم فارسی نیست چون حالت متن از راست به چپ و جود ندارد برای فارسی نوشتن باید از HTML استفاده کنید.\\r\\n{{{\\r\\n#!html\\r\\n<div dir=\"rtl\">\\r\\n}}}\\r\\nاین نمونه یک متن از راست به چپ فارسی است که در HTML نوشته شده تا اعداد 12345 و حروف لاتین ABCDEF در محل خودشان نمایش داده شوند.\\r\\n{{{\\r\\n#!html\\r\\n</div>\\r\\n}}}\\r\\n\\r\\n=== Polish ===\\r\\nPchnąć w tę łódź jeża lub osiem skrzyń fig; Nocna gżegżółka zawsze dzienną przekuka.\\r\\n\\r\\n=== Portuguese ===\\r\\nÉ possível guardar caracteres especias da língua portuguesa, incluindo o símbolo da moeda européia ''€'', trema ''ü'', crase ''à'', agudos ''áéíóú'', circunflexos ''âêô'', til ''ãõ'', cedilha ''ç'', ordinais ''ªº'', grau ''°¹²³''.\\r\\n\\r\\n=== Russian ===\\r\\nПроверка русского языка: кажется работает... И буква \"ё\" есть...\\r\\n\\r\\n=== Serbian ===\\r\\nPodržan, uprkos činjenici da se za njegovo pisanje koriste чак два алфабета.\\r\\n\\r\\n=== Slovenian ===\\r\\nTa suhi škafec pušča vodo že od nekdaj!\\r\\n\\r\\n=== Spanish ===\\r\\nEsto es un pequeño texto en Español, donde el veloz murciélago hindú comía cardlllo y kiwi\\r\\n\\r\\n=== Swedish ===\\r\\nRäven raskar över isen med luva på.\\r\\n\\r\\n=== Thai ===\\r\\nTrac แสดงภาษาไทยได้อย่างถูกต้อง!\\r\\n\\r\\n=== Ukrainian ===\\r\\nПеревірка української мови...\\r\\n\\r\\n=== Urdu ===\\r\\nٹریک اردو بھی سپورٹ کرتا ہے۔\\r\\n\\r\\n=== Vietnamese ===\\r\\nViết tiếng Việt cũng được.\\r\\n', NULL, NULL),\n('TracTimeline', 1, 1362994208331354, 'trac', '127.0.0.1', '= The Trac Timeline =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nThe timeline provides a historic view of the project in a single report.\\r\\n\\r\\nIt lists all Trac events that have occurred in chronological order, a\\r\\nbrief description of each event and if applicable, the person responsible for\\r\\nthe change.\\r\\n\\r\\nThe timeline lists these kinds of events:\\r\\n * ''''''Wiki page events'''''' — Creation and changes\\r\\n * ''''''Ticket events'''''' — Creation and resolution/closing (and optionally other changes)\\r\\n * ''''''Source code changes '''''' — Repository check-ins\\r\\n * ''''''Milestone '''''' — Milestone completed\\r\\n\\r\\nEach event entry provides a hyperlink to the specific event in question, who authored the change as well as\\r\\na brief excerpt of the actual comment or text, if available.\\r\\n\\r\\nIt is possible to filter the displayed events with the various filters in the option panel:\\r\\n * ''''View changes from'''' — the date from which to start displaying events (current date if empty). Events that occurred after this date will not be shown, only those that occurred before that date.\\r\\n * ''''and X days back'''' — how many days backwards in time to get events.\\r\\n * ''''done by'''' — the author of an event. It accepts a space-separated list of authors for which events should be included. Alternatively, if the author names are prefixed by a \"-\" character, then the events having those authors will be excluded, and all the others included. Single or double quotes can be used for specifying author names containing space characters. ''''(since 0.12)''''\\r\\n * ''''Changesets in all repositories'''' — if you have more than one repository connected to your Trac project, then you can filter the output so events from specific repositories are not shown. ''''(since 0.12)''''\\r\\n * ''''Milestones reached'''' — display or hide milestones reached.\\r\\n * ''''Opened and closed tickets'''' — display or hide ticket open or close events.\\r\\n * ''''Wiki changes'''' — display or hide Wiki change events.\\r\\n\\r\\nSee !TracIni''s [wiki:TracIni#timeline-section \"[timeline] section\"] for timeline configuration options.\\r\\n\\r\\n== RSS Support ==\\r\\n\\r\\nThe Timeline module supports subscription using RSS 2.0 syndication. To subscribe to project events, click the orange ''''''XML'''''' icon at the bottom of the page. See TracRss for more information on RSS support in Trac.\\r\\n\\r\\n----\\r\\nSee also: TracGuide, TracIni, TracWiki, WikiFormatting, TracRss, TracNotification\\r\\n', NULL, NULL);\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('TracPlugins', 1, 1362994208332815, 'trac', '127.0.0.1', '= Trac plugins =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nFrom version 0.9 onwards, Trac is extensible with [trac:PluginList plugins]. Plugin functionality is based on the [trac:TracDev/ComponentArchitecture component architecture], with peculiarities described in the [TracDev/PluginDevelopment plugin development] page.\\r\\n\\r\\n== Plugin discovery ==\\r\\n\\r\\nFrom the user''s point of view, a plugin is either a standalone .py file or an .egg package. Trac looks for plugins in the global shared plugins directory (see [TracIni#GlobalConfiguration Global Configuration]) and in the `plugins` directory of the local TracEnvironment. Components defined in globally-installed plugins should be explicitly enabled in the [[TracIni#components-section| [components] ]] section of the trac.ini file.\\r\\n\\r\\n== Requirements for Trac eggs ==\\r\\n\\r\\nTo use egg-based plugins in Trac, you need to have [http://peak.telecommunity.com/DevCenter/setuptools setuptools] (version 0.6) installed.\\r\\n\\r\\nTo install `setuptools`, download the bootstrap module [http://peak.telecommunity.com/dist/ez_setup.py ez_setup.py] and execute it as follows:\\r\\n\\r\\n{{{\\r\\n$ python ez_setup.py\\r\\n}}}\\r\\n\\r\\nIf the `ez_setup.py` script fails to install the setuptools release, you can download it from [http://www.python.org/pypi/setuptools PyPI] and install it manually.\\r\\n\\r\\nPlugins can also consist of a single `.py` file dropped directly into either the project''s or the shared `plugins` directory.\\r\\n\\r\\n== Installing a Trac plugin ==\\r\\n\\r\\n=== For a single project ===\\r\\n\\r\\nPlugins are typically packaged as [http://peak.telecommunity.com/DevCenter/PythonEggs Python eggs]. That means they are .zip archives with the file extension `.egg`.\\r\\n\\r\\nIf you have downloaded a source distribution of a plugin, and want to build the `.egg` file:\\r\\n\\r\\n * Unpack the source. It should provide `setup.py`.\\r\\n * Run:\\r\\n\\r\\n{{{\\r\\n$ python setup.py bdist_egg\\r\\n}}}\\r\\n\\r\\nYou should have a *.egg file. Examine the output of running python to find where this was created.\\r\\n\\r\\nOnce you have the plugin archive, copy it into the `plugins` directory of the [wiki:TracEnvironment project environment]. Also, make sure that the web server has sufficient permissions to read the plugin egg. Then restart the web server. If you are running as a [wiki:TracStandalone \"tracd\" standalone server], restart tracd (kill and run again).\\r\\n\\r\\nTo uninstall a plugin installed this way, remove the egg from the `plugins` directory and restart the web server.\\r\\n\\r\\nNote: the Python version that the egg is built with ''''must'''' match the Python version with which Trac is run. For example, if you''re running Trac under Python 2.5, but have upgraded your standalone Python to 2.6, the eggs won''t be recognized.\\r\\n\\r\\nNote also: in a multi-project setup, a pool of Python interpreter instances will be dynamically allocated to projects based on need; since plugins occupy a place in Python''s module system, the first version of any given plugin to be loaded will be used for all projects. In other words, you cannot use different versions of a single plugin in two projects of a multi-project setup. It may be safer to install plugins for all projects (see below), and then enable them selectively on a project-by-project basis.\\r\\n\\r\\n=== For all projects ===\\r\\n\\r\\n==== With an .egg file ====\\r\\n\\r\\nSome plugins (such as [trac:SpamFilter SpamFilter]) are downloadable as an `.egg` file that can be installed with the `easy_install` program:\\r\\n{{{\\r\\neasy_install TracSpamFilter\\r\\n}}}\\r\\n\\r\\nIf `easy_install` is not on your system, see the Requirements section above to install it. Windows users will need to add the `Scripts` directory of their Python installation (for example, `C:\\\\Python24\\\\Scripts`) to their `PATH` environment variable (see [http://peak.telecommunity.com/DevCenter/EasyInstall#windows-notes easy_install Windows notes] for more information).\\r\\n\\r\\nIf Trac reports permission errors after installing a zipped egg, and you would rather not bother providing a egg cache directory writable by the web server, you can get around it by simply unzipping the egg. Just pass `--always-unzip` to `easy_install`:\\r\\n{{{\\r\\neasy_install --always-unzip TracSpamFilter-0.4.1_r10106-py2.6.egg\\r\\n}}}\\r\\nYou should end up with a directory having the same name as the zipped egg (complete with `.egg` extension) and containing its uncompressed contents.\\r\\n\\r\\nTrac also searches for plugins installed in the shared plugins directory ''''(since 0.10)''''; see TracIni#GlobalConfiguration. This is a convenient way to share the installation of plugins across several, but not all, environments.\\r\\n\\r\\n==== From source ====\\r\\n\\r\\n`easy_install` makes installing from source a snap. Just give it the URL to either a Subversion repository or a tarball/zip of the source:\\r\\n{{{\\r\\neasy_install http://svn.edgewall.com/repos/trac/plugins/0.12/spam-filter-captcha\\r\\n}}}\\r\\n\\r\\n==== Enabling the plugin ====\\r\\n\\r\\nUnlike plugins installed per-environment, you''ll have to explicitly enable globally installed plugins via [wiki:TracIni trac.ini]. This also applies to plugins installed in the shared plugins directory, i.e. the path specified in the `[inherit] plugins_dir` configuration option. \\r\\n\\r\\nThis is done in the `[components]` section of the configuration file. For example:\\r\\n{{{\\r\\n[components]\\r\\ntracspamfilter.* = enabled\\r\\n}}}\\r\\n\\r\\nThe name of the option is the Python package of the plugin. This should be specified in the documentation of the plugin, but can also be easily discovered by looking at the source (look for a top-level directory that contains a file named `__init__.py`).\\r\\n\\r\\nNote: After installing the plugin, you must restart your web server.\\r\\n\\r\\n==== Uninstalling ====\\r\\n\\r\\n`easy_install` or `python setup.py` does not have an uninstall feature. Hower, it is usually quite trivial to remove a globally-installed egg and reference:\\r\\n\\r\\n 1. Do `easy_install -m [plugin name]` to remove references from `$PYTHONLIB/site-packages/easy-install.pth` when the plugin installed by setuptools.\\r\\n 1. Delete executables from `/usr/bin`, `/usr/local/bin`, or `C:\\\\\\\\Python*\\\\Scripts`. To find what executables are involved, refer to the `[console-script]` section of `setup.py`.\\r\\n 1. Delete the .egg file or folder from where it''s installed (usually inside `$PYTHONLIB/site-packages/`).\\r\\n 1. Restart the web server.\\r\\n\\r\\nIf you are uncertain about the location of the egg, here''s a small tip to help locate an egg (or any package). Just replace `myplugin` with whatever namespace the plugin uses (as used when enabling the plugin):\\r\\n{{{\\r\\n>>> import myplugin\\r\\n>>> print myplugin.__file__\\r\\n/opt/local/python24/lib/site-packages/myplugin-0.4.2-py2.4.egg/myplugin/__init__.pyc\\r\\n}}}\\r\\n\\r\\n== Setting up the plugin cache ==\\r\\n\\r\\nSome plugins will need to be extracted by the Python eggs runtime (`pkg_resources`), so that their contents are actual files on the file system. The directory in which they are extracted defaults to `.python-eggs` in the home directory of the current user, which may or may not be a problem. You can, however, override the default location using the `PYTHON_EGG_CACHE` environment variable.\\r\\n\\r\\nTo do this from the Apache configuration, use the `SetEnv` directive:\\r\\n{{{\\r\\nSetEnv PYTHON_EGG_CACHE /path/to/dir\\r\\n}}}\\r\\n\\r\\nThis works whether you''re using the [wiki:TracCgi CGI] or the [wiki:TracModPython mod_python] front-end. Put this directive next to where you set the path to the [wiki:TracEnvironment Trac environment], i.e. in the same `<Location>` block.\\r\\n\\r\\nFor example (for CGI):\\r\\n{{{\\r\\n <Location /trac>\\r\\n   SetEnv TRAC_ENV /path/to/projenv\\r\\n   SetEnv PYTHON_EGG_CACHE /path/to/dir\\r\\n </Location>\\r\\n}}}\\r\\n\\r\\nOr (for mod_python):\\r\\n{{{\\r\\n <Location /trac>\\r\\n   SetHandler mod_python\\r\\n   ...\\r\\n   SetEnv PYTHON_EGG_CACHE /path/to/dir\\r\\n </Location>\\r\\n}}}\\r\\n\\r\\n ''''Note: !SetEnv requires the `mod_env` module which needs to be activated for Apache. In this case the !SetEnv directive can also be used in the `mod_python` Location block.''''\\r\\n\\r\\nFor [wiki:TracFastCgi FastCGI], you''ll need to `-initial-env` option, or whatever is provided by your web server for setting environment variables. \\r\\n\\r\\n ''''Note: that if you already use -initial-env to set the project directory for either a single project or parent you will need to add an additional -initial-env directive to the !FastCgiConfig directive. I.e.\\r\\n\\r\\n{{{\\r\\nFastCgiConfig -initial-env TRAC_ENV=/var/lib/trac -initial-env PYTHON_EGG_CACHE=/var/lib/trac/plugin-cache\\r\\n}}}\\r\\n\\r\\n=== About hook scripts ===\\r\\n\\r\\nIf you''ve set up some subversion hook scripts that call the Trac engine, such as the post-commit hook script provided in the `/contrib` directory, make sure you define the `PYTHON_EGG_CACHE` environment variable within these scripts as well.\\r\\n\\r\\n== Troubleshooting ==\\r\\n\\r\\n=== Is setuptools properly installed? ===\\r\\n\\r\\nTry this from the command line:\\r\\n{{{\\r\\n$ python -c \"import pkg_resources\"\\r\\n}}}\\r\\n\\r\\nIf you get ''''''no output'''''', setuptools ''''''is'''''' installed. Otherwise, you''ll need to install it before plugins will work in Trac.\\r\\n\\r\\n=== Did you get the correct version of the Python egg? ===\\r\\n\\r\\nPython eggs have the Python version encoded in their filename. For example, `MyPlugin-1.0-py2.5.egg` is an egg for Python 2.5, and will ''''''not'''''' be loaded if you''re running a different Python version (such as 2.4 or 2.6).\\r\\n\\r\\nAlso, verify that the egg file you downloaded is indeed a .zip archive. If you downloaded it from a Trac site, chances are you downloaded the HTML preview page instead.\\r\\n\\r\\n=== Is the plugin enabled? ===\\r\\n\\r\\nIf you install a plugin globally (i.e., ''''not'''' inside the `plugins` directory of the Trac project environment), you must explicitly enable it in [TracIni trac.ini]. Make sure that:\\r\\n\\r\\n * ...you actually added the necessary line(s) to the `[components]` section.\\r\\n * ...the package/module names are correct.\\r\\n * ...the value is \"enabled\", not \"enable\" or \"Enable\".\\r\\n\\r\\n=== Check the permissions on the .egg file ===\\r\\n\\r\\nTrac must be able to read the .egg file. \\r\\n\\r\\n=== Check the log files ===\\r\\n\\r\\nEnable [wiki:TracLogging logging] and set the log level to `DEBUG`, then watch the log file for messages about loading plugins.\\r\\n\\r\\n=== Verify you have proper permissions ===\\r\\n\\r\\nSome plugins require you have special permissions in order to use them. [trac:WebAdmin WebAdmin], for example, requires the user to have TRAC_ADMIN permissions for it to show up on the navigation bar.\\r\\n\\r\\n=== Is the wrong version of the plugin loading? ===\\r\\n\\r\\nIf you put your plugins inside plugins directories, and certainly if you have more than one project, you need to make sure that the correct version of the plugin is loading. Here are some basic rules:\\r\\n\\r\\n * Only one version of the plugin can be loaded for each running Trac server (i.e., each Python process). The Python namespaces and module list will be shared, and it cannot handle duplicates. Whether a plugin is `enabled` or `disabled` makes no difference.\\r\\n * A globally-installed plugin (typically `setup.py install`) will override any version in the global or project plugins directories. A plugin from the global plugins directory will be located ''''before'''' any project plugins directory.\\r\\n * If your Trac server hosts more than one project (as with `TRAC_ENV_PARENT_DIR` setups), having two versions of a plugin in two different projects will give uncertain results. Only one of them will load, and the one loaded will be shared by both projects. Trac will load the first plugin found, usually from the project that receives the first request.\\r\\n * Having more than one version listed inside Python site-packages is fine (i.e., installed with `setup.py install`) -- setuptools will make sure you get the version installed most recently. However, don''t store more than one version inside a global or project plugins directory -- neither version number nor installed date will matter at all. There is no way to determine which one will be located first when Trac searches the directory for plugins.\\r\\n\\r\\n=== If all of the above failed ===\\r\\n\\r\\nOkay, so the logs don''t mention plugins, the egg is readable, the Python version is correct, ''''and'''' the egg has been installed globally (and is enabled in trac.ini)... and it ''''still'''' doesn''t work or give any error messages or any other indication as to why. Hop on the [trac:IrcChannel IrcChannel] and ask away!\\r\\n\\r\\n== Web-based plugin administration ==\\r\\n\\r\\nThe WebAdmin plugin (part of the core since 0.11) offers limited support for plugin configuration through the web to users with `TRAC_ADMIN` permission:\\r\\n\\r\\n* en/disabling installed plugins\\r\\n* installing plugins by uploading them as eggs\\r\\n\\r\\nYou probably want to disable the second function for security reasons: in `trac.ini`, in the `[components]` section, add the line\\r\\n{{{\\r\\ntrac.admin.web_ui.PluginAdminPanel = disabled\\r\\n}}}\\r\\nThis disables the whole panel, so the first function will no longer be available either.\\r\\n\\r\\n----\\r\\nSee also TracGuide, [trac:PluginList plugin list], [trac:TracDev/ComponentArchitecture component architecture].', NULL, NULL),\n('TracTicketsCustomFields', 1, 1362994208335124, 'trac', '127.0.0.1', '= Custom Ticket Fields =\\r\\nTrac supports adding custom, user-defined fields to the ticket module. Using custom fields, you can add typed, site-specific properties to tickets.\\r\\n\\r\\n== Configuration ==\\r\\nConfiguring custom ticket fields is done in the [wiki:TracIni trac.ini] file. All field definitions should be under a section named `[ticket-custom]`.\\r\\n\\r\\nThe syntax of each field definition is:\\r\\n{{{\\r\\n FIELD_NAME = TYPE\\r\\n (FIELD_NAME.OPTION = VALUE)\\r\\n ...\\r\\n}}}\\r\\nThe example below should help to explain the syntax.\\r\\n\\r\\n=== Available Field Types and Options ===\\r\\n * ''''''text'''''': A simple (one line) text field.\\r\\n   * label: Descriptive label.\\r\\n   * value: Default value.\\r\\n   * order: Sort order placement. (Determines relative placement in forms with respect to other custom fields.)\\r\\n   * format: One of: \\r\\n     * `plain` for plain text \\r\\n     * `wiki` to interpret the content as WikiFormatting (''''since 0.11.3'''') \\r\\n     * `reference` to treat the content as a queryable value (''''since 1.0'''') \\r\\n     * `list` to interpret the content as a list of queryable values, separated by whitespace (''''since 1.0'''')\\r\\n * ''''''checkbox'''''': A boolean value check box.\\r\\n   * label: Descriptive label.\\r\\n   * value: Default value (0 or 1).\\r\\n   * order: Sort order placement.\\r\\n * ''''''select'''''': Drop-down select box. Uses a list of values.\\r\\n   * label: Descriptive label.\\r\\n   * options: List of values, separated by ''''''|'''''' (vertical pipe).\\r\\n   * value: Default value (one of the values from options).\\r\\n   * order: Sort order placement.\\r\\n * ''''''radio'''''': Radio buttons. Essentially the same as ''''''select''''''.\\r\\n   * label: Descriptive label.\\r\\n   * options: List of values, separated by ''''''|'''''' (vertical pipe).\\r\\n   * value: Default value (one of the values from options).\\r\\n   * order: Sort order placement.\\r\\n * ''''''textarea'''''': Multi-line text area.\\r\\n   * label: Descriptive label.\\r\\n   * value: Default text.\\r\\n   * cols: Width in columns.\\r\\n   * rows: Height in lines.\\r\\n   * order: Sort order placement.\\r\\n   * format: Either `plain` for plain text or `wiki` to interpret the content as WikiFormatting. (''''since 0.11.3'''')\\r\\n\\r\\n=== Sample Config ===\\r\\n{{{\\r\\n[ticket-custom]\\r\\n\\r\\ntest_one = text\\r\\ntest_one.label = Just a text box\\r\\n\\r\\ntest_two = text\\r\\ntest_two.label = Another text-box\\r\\ntest_two.value = Default [mailto:joe@nospam.com owner]\\r\\ntest_two.format = wiki\\r\\n\\r\\ntest_three = checkbox\\r\\ntest_three.label = Some checkbox\\r\\ntest_three.value = 1\\r\\n\\r\\ntest_four = select\\r\\ntest_four.label = My selectbox\\r\\ntest_four.options = one|two|third option|four\\r\\ntest_four.value = two\\r\\n\\r\\ntest_five = radio\\r\\ntest_five.label = Radio buttons are fun\\r\\ntest_five.options = uno|dos|tres|cuatro|cinco\\r\\ntest_five.value = dos\\r\\n\\r\\ntest_six = textarea\\r\\ntest_six.label = This is a large textarea\\r\\ntest_six.value = Default text\\r\\ntest_six.cols = 60\\r\\ntest_six.rows = 30\\r\\n}}}\\r\\n\\r\\n''''Note: To make entering an option for a `select` type field optional, specify a leading `|` in the `fieldname.options` option.''''\\r\\n\\r\\n=== Reports Involving Custom Fields ===\\r\\n\\r\\nCustom ticket fields are stored in the `ticket_custom` table, not in the `ticket` table. So to display the values from custom fields in a report, you will need a join on the 2 tables. Let''s use an example with a custom ticket field called `progress`.\\r\\n\\r\\n{{{\\r\\n#!sql\\r\\nSELECT p.value AS __color__,\\r\\n   id AS ticket, summary, owner, c.value AS progress\\r\\n  FROM ticket t, enum p, ticket_custom c\\r\\n  WHERE status IN (''assigned'') AND t.id = c.ticket AND c.name = ''progress''\\r\\nAND p.name = t.priority AND p.type = ''priority''\\r\\n  ORDER BY p.value\\r\\n}}}\\r\\n''''''Note'''''' that this will only show tickets that have progress set in them, which is ''''''not the same as showing all tickets''''''. If you created this custom ticket field ''''after'''' you have already created some tickets, they will not have that field defined, and thus they will never show up on this ticket query. If you go back and modify those tickets, the field will be defined, and they will appear in the query. If that''s all you want, you''re set.\\r\\n\\r\\nHowever, if you want to show all ticket entries (with progress defined and without), you need to use a `JOIN` for every custom field that is in the query.\\r\\n{{{\\r\\n#!sql\\r\\nSELECT p.value AS __color__,\\r\\n   id AS ticket, summary, component, version, milestone, severity,\\r\\n   (CASE status WHEN ''assigned'' THEN owner||'' *'' ELSE owner END) AS owner,\\r\\n   time AS created,\\r\\n   changetime AS _changetime, description AS _description,\\r\\n   reporter AS _reporter,\\r\\n  (CASE WHEN c.value = ''0'' THEN ''None'' ELSE c.value END) AS progress\\r\\n  FROM ticket t\\r\\n     LEFT OUTER JOIN ticket_custom c ON (t.id = c.ticket AND c.name = ''progress'')\\r\\n     JOIN enum p ON p.name = t.priority AND p.type=''priority''\\r\\n  WHERE status IN (''new'', ''assigned'', ''reopened'')\\r\\n  ORDER BY p.value, milestone, severity, time\\r\\n}}}\\r\\n\\r\\nNote in particular the `LEFT OUTER JOIN` statement here.\\r\\n\\r\\n=== Updating the database ===\\r\\n\\r\\nAs noted above, any tickets created before a custom field has been defined will not have a value for that field. Here''s a bit of SQL (tested with SQLite) that you can run directly on the Trac database to set an initial value for custom ticket fields. Inserts the default value of ''None'' into a custom field called ''request_source'' for all tickets that have no existing value:\\r\\n\\r\\n{{{\\r\\n#!sql\\r\\nINSERT INTO ticket_custom\\r\\n   (ticket, name, value)\\r\\n   SELECT \\r\\n      id AS ticket,\\r\\n      ''request_source'' AS name,\\r\\n      ''None'' AS value\\r\\n   FROM ticket \\r\\n   WHERE id NOT IN (\\r\\n      SELECT ticket FROM ticket_custom\\r\\n   );\\r\\n}}}\\r\\n\\r\\nIf you added multiple custom fields at different points in time, you should be more specific in the subquery on table {{{ticket}}} by adding the exact custom field name to the query:\\r\\n\\r\\n{{{\\r\\n#!sql\\r\\nINSERT INTO ticket_custom\\r\\n   (ticket, name, value)\\r\\n   SELECT \\r\\n      id AS ticket,\\r\\n      ''request_source'' AS name,\\r\\n      ''None'' AS value\\r\\n   FROM ticket \\r\\n   WHERE id NOT IN (\\r\\n      SELECT ticket FROM ticket_custom WHERE name = ''request_source''\\r\\n   );\\r\\n}}}\\r\\n\\r\\n----\\r\\nSee also: TracTickets, TracIni', NULL, NULL),\n('WikiStart', 1, 1362994208337041, 'trac', '127.0.0.1', '= Welcome to Trac 1.0.1 =\\r\\n\\r\\nTrac is a ''''''minimalistic'''''' approach to ''''''web-based'''''' management of\\r\\n''''''software projects''''''. Its goal is to simplify effective tracking and handling of software issues, enhancements and overall progress.\\r\\n\\r\\nAll aspects of Trac have been designed with the single goal to \\r\\n''''''help developers write great software'''''' while ''''''staying out of the way''''''\\r\\nand imposing as little as possible on a team''s established process and\\r\\nculture.\\r\\n\\r\\nAs all Wiki pages, this page is editable, this means that you can\\r\\nmodify the contents of this page simply by using your\\r\\nweb-browser. Simply click on the \"Edit this page\" link at the bottom\\r\\nof the page. WikiFormatting will give you a detailed description of\\r\\navailable Wiki formatting commands.\\r\\n\\r\\n\"[wiki:TracAdmin trac-admin] ''''yourenvdir'''' initenv\" created\\r\\na new Trac environment, containing a default set of wiki pages and some sample\\r\\ndata. This newly created environment also contains \\r\\n[wiki:TracGuide documentation] to help you get started with your project.\\r\\n\\r\\nYou can use [wiki:TracAdmin trac-admin] to configure\\r\\n[http://trac.edgewall.org/ Trac] to better fit your project, especially in\\r\\nregard to ''''components'''', ''''versions'''' and ''''milestones''''. \\r\\n\\r\\n\\r\\nTracGuide is a good place to start.\\r\\n\\r\\nEnjoy! [[BR]]\\r\\n''''The Trac Team''''\\r\\n\\r\\n== Starting Points ==\\r\\n\\r\\n * TracGuide --  Built-in Documentation\\r\\n * [http://trac.edgewall.org/ The Trac project] -- Trac Open Source Project\\r\\n * [http://trac.edgewall.org/wiki/TracFaq Trac FAQ] -- Frequently Asked Questions\\r\\n * TracSupport --  Trac Support\\r\\n\\r\\nFor a complete list of local wiki pages, see TitleIndex.\\r\\n', NULL, NULL),\n('WikiNewPage', 1, 1362994208338509, 'trac', '127.0.0.1', '= Steps to Add a New Wiki Page =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nNote: make sure you actually have the rights to edit wiki pages. If you don''t see the **Edit this page** button, read the information relative to the editing policy for your Trac installation (usually on the front page WikiStart), or contact your local Trac administrator.\\r\\n\\r\\n 1. Choose a name for your new page. See WikiPageNames for naming conventions.\\r\\n 2. Edit an existing page (or any other resources that support WikiFormatting and add a [TracLinks link] to your new page. Save your changes.\\r\\n 3. Follow the link you created to take you to the new page. Trac will display a \"describe !PageName here\" message.\\r\\n 4. Click the \"Edit this page\" button to edit and add content to your new page. Save your changes.\\r\\n 5. All done. Your new page is published.\\r\\n\\r\\nYou can skip the second step by entering the CamelCase name of the page in the quick-search field at the top of the page. But note that the page will effectively be \"orphaned\" unless you link to it from somewhere else.\\r\\n\\r\\n== Rename a page #renaming\\r\\n\\r\\nWhile picking up good WikiPageNames is important, you can always change your mind\\r\\nand rename the page later.\\r\\n\\r\\nYou''ll need to ask for the WIKI_RENAME permission in order to be allowed to do this.\\r\\nWhen renaming a page, you''ll be offered the possibility to create a redirection page, so that links pointing to the old location will not be left dangling.\\r\\n\\r\\n----\\r\\nSee also: TracWiki, PageTemplates, WikiFormatting, TracLinks, WikiDeletePage\\r\\n', NULL, NULL),\n('InterTrac', 1, 1362994208339918, 'trac', '127.0.0.1', '= InterTrac Links  =\\r\\n\\r\\nTrac supports a convenient way to refer to resources of other Trac servers, from within the Wiki markup, since version 0.10.\\r\\n\\r\\n== Definitions ==\\r\\n\\r\\nAn InterTrac link can be seen as a scoped TracLinks.\\r\\nIt is used for referring to a Trac resource \\r\\n(Wiki page, changeset, ticket, ...) located in another\\r\\nTrac environment.\\r\\n\\r\\n== List of Active InterTrac Prefixes ==\\r\\n\\r\\n[[InterTrac]]\\r\\n\\r\\n== Link Syntax ==\\r\\n\\r\\nSimply use the name of the other Trac environment as a prefix, \\r\\nfollowed by a colon, ending with the resource located in the other environment.\\r\\n\\r\\n{{{\\r\\n<target_environment>:<TracLinks>\\r\\n}}}\\r\\n\\r\\nThe other resource is specified using a regular TracLinks, of any flavor.\\r\\n\\r\\nThat target environment name is either the real name of the \\r\\nenvironment, or an alias for it. \\r\\nThe aliases are defined in `trac.ini` (see below).\\r\\nThe prefix is case insensitive.\\r\\n\\r\\nIf the InterTrac link is enclosed in square brackets (like `[th:WikiExtrasPlugin]`), the InterTrac prefix is removed in the displayed link, like a normal link resolver would be (i.e. the above would be displayed as `WikiExtrasPlugin`).\\r\\n\\r\\nFor convenience, there''s also some alternative short-hand form, \\r\\nwhere one can use an alias as an immediate prefix \\r\\nfor the identifier of a ticket, changeset or report:\\r\\n(e.g. `#T234`, `[T1508]`, `[trac 1508]`, ...)\\r\\n\\r\\n== Examples ==\\r\\n\\r\\nIt is necessary to setup a configuration for the InterTrac facility.\\r\\nThis configuration has to be done in the TracIni file, `[intertrac]` section.\\r\\n\\r\\nExample configuration:\\r\\n{{{\\r\\n...\\r\\n[intertrac]\\r\\n# -- Example of setting up an alias:\\r\\nt = trac\\r\\n\\r\\n# -- Link to an external Trac:\\r\\ntrac.title = Edgewall''s Trac for Trac\\r\\ntrac.url = http://trac.edgewall.org\\r\\n}}}\\r\\n\\r\\nThe `.url` is mandatory and is used for locating the other Trac.\\r\\nThis can be a relative URL in case that Trac environment is located \\r\\non the same server.\\r\\n\\r\\nThe `.title` information will be used for providing an useful tooltip\\r\\nwhen moving the cursor over an InterTrac links.\\r\\n\\r\\nFinally, the `.compat` option can be used to activate or disable\\r\\na ''''compatibility'''' mode:\\r\\n * If the targeted Trac is running a version below [trac:milestone:0.10 0.10] \\r\\n   ([trac:r3526 r3526] to be precise), then it doesn''t know how to dispatch an InterTrac \\r\\n   link, and it''s up to the local Trac to prepare the correct link. \\r\\n   Not all links will work that way, but the most common do. \\r\\n   This is called the compatibility mode, and is `true` by default. \\r\\n * If you know that the remote Trac knows how to dispatch InterTrac links, \\r\\n   you can explicitly disable this compatibility mode and then ''''any'''' \\r\\n   TracLinks can become an InterTrac link.\\r\\n\\r\\nNow, given the above configuration, one could create the following links:\\r\\n * to this InterTrac page:\\r\\n   * `trac:wiki:InterTrac` trac:wiki:InterTrac\\r\\n   * `t:wiki:InterTrac` t:wiki:InterTrac\\r\\n   * Keys are case insensitive: `T:wiki:InterTrac` T:wiki:InterTrac\\r\\n * to the ticket #234:\\r\\n   * `trac:ticket:234` trac:ticket:234\\r\\n   * `trac:#234` trac:#234 \\r\\n   * `#T234` #T234\\r\\n * to the changeset [1912]:\\r\\n   * `trac:changeset:1912` trac:changeset:1912\\r\\n   * `[T1912]` [T1912]\\r\\n * to the log range [3300:3330]: ''''''(Note: the following ones need `trac.compat=false`)''''''\\r\\n   * `trac:log:@3300:3330` trac:log:@3300:3330  \\r\\n   * `[trac 3300:3330]` [trac 3300:3330] \\r\\n * finally, to link to the start page of a remote trac, simply use its prefix followed by '':'', inside an explicit link. Example: `[th: Trac Hacks]` (''''since 0.11; note that the ''''remote'''' Trac has to run 0.11 for this to work'''')\\r\\n\\r\\nThe generic form `intertrac_prefix:module:id` is translated\\r\\nto the corresponding URL `<remote>/module/id`, shorthand links\\r\\nare specific to some modules (e.g. !#T234 is processed by the\\r\\nticket module) and for the rest (`intertrac_prefix:something`),\\r\\nwe rely on the TracSearch#quickjump facility of the remote Trac.\\r\\n\\r\\n----\\r\\nSee also: TracLinks, InterWiki\\r\\n', NULL, NULL),\n('TracQuery', 1, 1362994208341578, 'trac', '127.0.0.1', '= Trac Ticket Queries =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nIn addition to [wiki:TracReports reports], Trac provides support for ''''custom ticket queries'''', used to display lists of tickets meeting a specified set of criteria. \\r\\n\\r\\nTo configure and execute a custom query, switch to the ''''View Tickets'''' module from the navigation bar, and select the ''''Custom Query'''' link.\\r\\n\\r\\n== Filters ==\\r\\n\\r\\nWhen you first go to the query page the default filter will display tickets relevant to you:\\r\\n * If logged in then all open tickets it will display open tickets assigned to you.\\r\\n * If not logged in but you have specified a name or email address in the preferences then it will display all open tickets where your email (or name if email not defined) is in the CC list.\\r\\n * If not logged and no name/email defined in the preferences then all open issues are displayed.\\r\\n\\r\\nCurrent filters can be removed by clicking the button to the left with the minus sign on the label.  New filters are added from the pulldown lists at the bottom corners of the filters box (''And'' conditions on the left, ''Or'' conditions on the right).  Filters with either a text box or a pulldown menu of options can be added multiple times to perform an ''''or'''' of the criteria.\\r\\n\\r\\nYou can use the fields just below the filters box to group the results based on a field, or display the full description for each ticket.\\r\\n\\r\\nOnce you''ve edited your filters click the ''''Update'''' button to refresh your results.\\r\\n\\r\\n== Navigating Tickets ==\\r\\nClicking on one of the query results will take you to that ticket.  You can navigate through the results by clicking the ''''Next Ticket'''' or ''''Previous Ticket'''' links just below the main menu bar, or click the ''''Back to Query'''' link to return to the query page.  \\r\\n\\r\\nYou can safely edit any of the tickets and continue to navigate through the results using the ''''!Next/Previous/Back to Query'''' links after saving your results.  When you return to the query ''''any tickets which were edited'''' will be displayed with italicized text.  If one of the tickets was edited such that [[html(<span style=\"color: grey\">it no longer matches the query criteria </span>)]] the text will also be greyed. Lastly, if ''''''a new ticket matching the query criteria has been created'''''', it will be shown in bold. \\r\\n\\r\\nThe query results can be refreshed and cleared of these status indicators by clicking the ''''Update'''' button again.\\r\\n\\r\\n== Saving Queries ==\\r\\n\\r\\nTrac allows you to save the query as a named query accessible from the reports module. To save a query ensure that you have ''''Updated'''' the view and then click the ''''Save query'''' button displayed beneath the results.\\r\\nYou can also save references to queries in Wiki content, as described below.\\r\\n\\r\\n''''Note:'''' one way to easily build queries like the ones below, you can build and test the queries in the Custom report module and when ready - click ''''Save query''''. This will build the query string for you. All you need to do is remove the extra line breaks.\\r\\n\\r\\n''''Note:'''' you must have the ''''''REPORT_CREATE'''''' permission in order to save queries to the list of default reports. The ''''Save query'''' button will only appear if you are logged in as a user that has been granted this permission. If your account does not have permission to create reports, you can still use the methods below to save a query.\\r\\n\\r\\n\\r\\n=== Using TracLinks ===\\r\\n\\r\\nYou may want to save some queries so that you can come back to them later.  You can do this by making a link to the query from any Wiki page.\\r\\n{{{\\r\\n[query:status=new|assigned|reopened&version=1.0 Active tickets against 1.0]\\r\\n}}}\\r\\n\\r\\nWhich is displayed as:\\r\\n  [query:status=new|assigned|reopened&version=1.0 Active tickets against 1.0]\\r\\n\\r\\nThis uses a very simple query language to specify the criteria (see [wiki:TracQuery#QueryLanguage Query Language]).\\r\\n\\r\\nAlternatively, you can copy the query string of a query and paste that into the Wiki link, including the leading `?` character:\\r\\n{{{\\r\\n[query:?status=new&status=assigned&status=reopened&group=owner Assigned tickets by owner]\\r\\n}}}\\r\\n\\r\\nWhich is displayed as:\\r\\n  [query:?status=new&status=assigned&status=reopened&group=owner Assigned tickets by owner]\\r\\n\\r\\n=== Using the `[[TicketQuery]]` Macro ===\\r\\n\\r\\nThe [trac:TicketQuery TicketQuery] macro lets you display lists of tickets matching certain criteria anywhere you can use WikiFormatting.\\r\\n\\r\\nExample:\\r\\n{{{\\r\\n[[TicketQuery(version=0.6|0.7&resolution=duplicate)]]\\r\\n}}}\\r\\n\\r\\nThis is displayed as:\\r\\n  [[TicketQuery(version=0.6|0.7&resolution=duplicate)]]\\r\\n\\r\\nJust like the [wiki:TracQuery#UsingTracLinks query: wiki links], the parameter of this macro expects a query string formatted according to the rules of the simple [wiki:TracQuery#QueryLanguage ticket query language]. This also allows displaying the link and description of a single ticket:\\r\\n{{{\\r\\n[[TicketQuery(id=123)]]\\r\\n}}}\\r\\n\\r\\nThis is displayed as:\\r\\n  [[TicketQuery(id=123)]]\\r\\n\\r\\nA more compact representation without the ticket summaries is also available:\\r\\n{{{\\r\\n[[TicketQuery(version=0.6|0.7&resolution=duplicate, compact)]]\\r\\n}}}\\r\\n\\r\\nThis is displayed as:\\r\\n  [[TicketQuery(version=0.6|0.7&resolution=duplicate, compact)]]\\r\\n\\r\\nFinally, if you wish to receive only the number of defects that match the query, use the ``count`` parameter.\\r\\n\\r\\n{{{\\r\\n[[TicketQuery(version=0.6|0.7&resolution=duplicate, count)]]\\r\\n}}}\\r\\n\\r\\nThis is displayed as:\\r\\n  [[TicketQuery(version=0.6|0.7&resolution=duplicate, count)]]\\r\\n\\r\\n=== Customizing the ''''table'''' format ===\\r\\nYou can also customize the columns displayed in the table format (''''format=table'''') by using ''''col=<field>'''' - you can specify multiple fields and what order they are displayed by placing pipes (`|`) between the columns like below:\\r\\n\\r\\n{{{\\r\\n[[TicketQuery(max=3,status=closed,order=id,desc=1,format=table,col=resolution|summary|owner|reporter)]]\\r\\n}}}\\r\\n\\r\\nThis is displayed as:\\r\\n[[TicketQuery(max=3,status=closed,order=id,desc=1,format=table,col=resolution|summary|owner|reporter)]]\\r\\n\\r\\n==== Full rows ====\\r\\nIn ''''table'''' format you can also have full rows by using ''''rows=<field>'''' like below:\\r\\n\\r\\n{{{\\r\\n[[TicketQuery(max=3,status=closed,order=id,desc=1,format=table,col=resolution|summary|owner|reporter,rows=description)]]\\r\\n}}}\\r\\n\\r\\nThis is displayed as:\\r\\n[[TicketQuery(max=3,status=closed,order=id,desc=1,format=table,col=resolution|summary|owner|reporter,rows=description)]]\\r\\n\\r\\n\\r\\n=== Query Language ===\\r\\n\\r\\n`query:` TracLinks and the `[[TicketQuery]]` macro both use a mini “query language” for specifying query filters. Basically, the filters are separated by ampersands (`&`). Each filter then consists of the ticket field name, an operator, and one or more values. More than one value are separated by a pipe (`|`), meaning that the filter matches any of the values. To include a literal `&` or `|` in a value, escape the character with a backslash (`\\\\`).\\r\\n\\r\\nThe available operators are:\\r\\n|| ''''''`=`'''''' || the field content exactly matches one of the values ||\\r\\n|| ''''''`~=`'''''' || the field content contains one or more of the values ||\\r\\n|| ''''''`^=`'''''' || the field content starts with one of the values ||\\r\\n|| ''''''`$=`'''''' || the field content ends with one of the values ||\\r\\n\\r\\nAll of these operators can also be negated:\\r\\n|| ''''''`!=`'''''' || the field content matches none of the values ||\\r\\n|| ''''''`!~=`'''''' || the field content does not contain any of the values ||\\r\\n|| ''''''`!^=`'''''' || the field content does not start with any of the values ||\\r\\n|| ''''''`!$=`'''''' || the field content does not end with any of the values ||\\r\\n\\r\\nThe date fields `created` and `modified` can be constrained by using the `=` operator and specifying a value containing two dates separated by two dots (`..`). Either end of the date range can be left empty, meaning that the corresponding end of the range is open. The date parser understands a few natural date specifications like \"3 weeks ago\", \"last month\" and \"now\", as well as Bugzilla-style date specifications like \"1d\", \"2w\", \"3m\" or \"4y\" for 1 day, 2 weeks, 3 months and 4 years, respectively. Spaces in date specifications can be left out to avoid having to quote the query string. \\r\\n|| ''''''`created=2007-01-01..2008-01-01`'''''' || query tickets created in 2007 ||\\r\\n|| ''''''`created=lastmonth..thismonth`'''''' || query tickets created during the previous month ||\\r\\n|| ''''''`modified=1weekago..`'''''' || query tickets that have been modified in the last week ||\\r\\n|| ''''''`modified=..30daysago`'''''' || query tickets that have been inactive for the last 30 days ||\\r\\n\\r\\n----\\r\\nSee also: TracTickets, TracReports, TracGuide\\r\\n', NULL, NULL),\n('WikiProcessors', 1, 1362994208343496, 'trac', '127.0.0.1', '= Wiki Processors =\\r\\n\\r\\nProcessors are WikiMacros designed to provide alternative markup formats for the [TracWiki Wiki engine]. Processors can be thought of as ''''macro functions to process user-edited text''''. \\r\\n\\r\\nWiki processors can be used in any Wiki text throughout Trac,\\r\\nfor various different purposes, like:\\r\\n - [#CodeHighlightingSupport syntax highlighting] or for rendering text verbatim,\\r\\n - rendering [#HTMLrelated Wiki markup inside a context], \\r\\n   like inside <div> blocks or <span> or within <td> or <th> table cells,\\r\\n - using an alternative markup syntax, like [wiki:WikiHtml raw HTML] and\\r\\n   [wiki:WikiRestructuredText Restructured Text],\\r\\n   or [http://www.textism.com/tools/textile/ textile]\\r\\n\\r\\n\\r\\n== Using Processors ==\\r\\n\\r\\nTo use a processor on a block of text, first delimit the lines using\\r\\na Wiki ''''code block'''':\\r\\n{{{\\r\\n{{{\\r\\nThe lines\\r\\nthat should be processed...\\r\\n}}}\\r\\n}}}\\r\\n\\r\\nImmediately after the `{{{` or on the line just below, \\r\\nadd `#!` followed by the ''''processor name''''.\\r\\n\\r\\n{{{\\r\\n{{{\\r\\n#!processorname\\r\\nThe lines\\r\\nthat should be processed...\\r\\n}}}\\r\\n}}}\\r\\n\\r\\nThis is the \"shebang\" notation, familiar to most UNIX users.\\r\\n\\r\\nBesides their content, some Wiki processors can also accept ''''parameters'''',\\r\\nwhich are then given as `key=value` pairs after the processor name, \\r\\non the same line. If `value` has to contain space, as it''s often the case for\\r\\nthe style parameter, a quoted string can be used (`key=\"value with space\"`).\\r\\n\\r\\nAs some processors are meant to process Wiki markup, it''s quite possible to\\r\\n''''nest'''' processor blocks.\\r\\nYou may want to indent the content of nested blocks for increased clarity,\\r\\nthis extra indentation will be ignored when processing the content.\\r\\n\\r\\n\\r\\n== Examples ==\\r\\n\\r\\n||= Wiki Markup =||= Display =||\\r\\n{{{#!td colspan=2 align=center style=\"border: none\"\\r\\n\\r\\n                __Example 1__: Inserting raw HTML\\r\\n}}}\\r\\n|-----------------------------------------------------------------\\r\\n{{{#!td style=\"border: none\"\\r\\n{{{\\r\\n{{{\\r\\n#!html\\r\\n<h1 style=\"color: grey\">This is raw HTML</h1>\\r\\n}}}\\r\\n}}}\\r\\n}}}\\r\\n{{{#!td valign=top style=\"border: none; padding-left: 2em\"\\r\\n{{{\\r\\n#!html\\r\\n<h1 style=\"color: grey\">This is raw HTML</h1>\\r\\n}}}\\r\\n}}}\\r\\n|-----------------------------------------------------------------\\r\\n{{{#!td colspan=2 align=center style=\"border: none\"\\r\\n\\r\\n     __Example 2__: Highlighted Python code in a <div> block with custom style\\r\\n}}}\\r\\n|-----------------------------------------------------------------\\r\\n{{{#!td style=\"border: none\"\\r\\n  {{{\\r\\n  {{{#!div style=\"background: #ffd; border: 3px ridge\"\\r\\n\\r\\n  This is an example of embedded \"code\" block:\\r\\n\\r\\n    {{{\\r\\n    #!python\\r\\n    def hello():\\r\\n        return \"world\"\\r\\n    }}}\\r\\n\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td valign=top style=\"border: none; padding: 1em\"\\r\\n  {{{#!div style=\"background: #ffd; border: 3px ridge\"\\r\\n\\r\\n  This is an example of embedded \"code\" block:\\r\\n\\r\\n    {{{\\r\\n    #!python\\r\\n    def hello():\\r\\n        return \"world\"\\r\\n    }}}\\r\\n\\r\\n  }}}\\r\\n}}}\\r\\n|-----------------------------------------------------------------\\r\\n{{{#!td colspan=2 align=center style=\"border: none\"\\r\\n\\r\\n     __Example 3__: Searching tickets from a wiki page, by keywords.\\r\\n}}}\\r\\n|-----------------------------------------------------------------\\r\\n{{{#!td style=\"border: none\"\\r\\n  {{{\\r\\n  {{{\\r\\n  #!html\\r\\n  <form action=\"/query\" method=\"get\"><div>\\r\\n  <input type=\"text\" name=\"keywords\" value=\"~\" size=\"30\"/>\\r\\n  <input type=\"submit\" value=\"Search by Keywords\"/>\\r\\n  <!-- To control what fields show up use hidden fields\\r\\n  <input type=\"hidden\" name=\"col\" value=\"id\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"summary\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"status\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"milestone\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"version\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"owner\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"priority\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"component\"/>\\r\\n  -->\\r\\n  </div></form>\\r\\n  }}}\\r\\n  }}}\\r\\n}}}\\r\\n{{{#!td valign=top style=\"border: none; padding: 1em\"\\r\\n  {{{\\r\\n  #!html\\r\\n  <form action=\"/query\" method=\"get\"><div>\\r\\n  <input type=\"text\" name=\"keywords\" value=\"~\" size=\"30\"/>\\r\\n  <input type=\"submit\" value=\"Search by Keywords\"/>\\r\\n  <!-- To control what fields show up use hidden fields\\r\\n  <input type=\"hidden\" name=\"col\" value=\"id\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"summary\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"status\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"milestone\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"version\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"owner\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"priority\"/>\\r\\n  <input type=\"hidden\" name=\"col\" value=\"component\"/>\\r\\n  -->\\r\\n  </div></form>\\r\\n  }}}\\r\\n}}}\\r\\n== Available Processors ==\\r\\n\\r\\nThe following processors are included in the Trac distribution:\\r\\n\\r\\n|| ''''''`#!default`'''''' || Present the text verbatim in a preformatted text block. This is the same as specifying ''''no'''' processor name (and no `#!`) ||\\r\\n|| ''''''`#!comment`'''''' || Do not process the text in this section (i.e. contents exist only in the plain text - not in the rendered page). ||\\r\\n|||| ||\\r\\n||||= ''''''HTML related'''''' =||\\r\\n|| ''''''`#!html`'''''' || Insert custom HTML in a wiki page. ||\\r\\n|| ''''''`#!htmlcomment`'''''' || Insert an HTML comment in a wiki page (''''since 0.12''''). ||\\r\\n|| || Note that `#!html` blocks have to be ''''self-contained'''', i.e. you can''t start an HTML element in one block and close it later in a second block. Use the following processors for achieving a similar effect.  ||\\r\\n|| ''''''`#!div`'''''' || Wrap an arbitrary Wiki content inside a <div> element (''''since 0.11''''). ||\\r\\n|| ''''''`#!span`'''''' || Wrap an arbitrary Wiki content inside a <span> element (''''since 0.11''''). ||\\r\\n|| ''''''`#!td`'''''' || Wrap an arbitrary Wiki content inside a <td> element (''''since 0.12'''') ||\\r\\n|| ''''''`#!th`'''''' || Wrap an arbitrary Wiki content inside a <th> element (''''since 0.12'''') ||\\r\\n|| ''''''`#!tr`'''''' || Can optionally be used for wrapping `#!td` and `#!th` blocks, either for specifying row attributes of better visual grouping (''''since 0.12'''') ||\\r\\n|| || See WikiHtml for example usage and more details about these processors. ||\\r\\n|||| ||\\r\\n||||= ''''''Other Markups'''''' =||\\r\\n|| ''''''`#!rst`'''''' || Trac support for Restructured Text. See WikiRestructuredText. ||\\r\\n|| ''''''`#!textile`'''''' || Supported if [http://cheeseshop.python.org/pypi/textile Textile] is installed. See [http://www.textism.com/tools/textile/ a Textile reference]. ||\\r\\n|||| ||\\r\\n||||= ''''''Code Highlighting Support'''''' =||\\r\\n|| ''''''`#!c`'''''' [[BR]] ''''''`#!cpp`'''''' (C++) [[BR]] ''''''`#!python`'''''' [[BR]] ''''''`#!perl`'''''' [[BR]] ''''''`#!ruby`'''''' [[BR]] ''''''`#!php`'''''' [[BR]] ''''''`#!asp`'''''' [[BR]] ''''''`#!java`'''''' [[BR]] ''''''`#!js`'''''' (Javascript) [[BR]] ''''''`#!sql`'''''' [[BR]] ''''''`#!xml`'''''' (XML or HTML) [[BR]] ''''''`#!sh`'''''' (!Bourne/Bash shell) [[BR]] ''''''etc.'''''' [[BR]] || Trac includes processors to provide inline syntax highlighting for source code in various languages. [[BR]] [[BR]] Trac relies on external software packages for syntax coloring, like [http://pygments.org Pygments]. [[BR]] [[BR]] See TracSyntaxColoring for information about which languages are supported and how to enable support for more languages. ||\\r\\n|||| ||\\r\\n\\r\\nUsing the MIME type as processor, it is possible to syntax-highlight the same languages that are supported when browsing source code.\\r\\n\\r\\n||||= ''''''MIME Type Processors'''''' =||\\r\\n{{{#!tr\\r\\n{{{#!td\\r\\nSome examples:\\r\\n {{{\\r\\n{{{\\r\\n#!text/html\\r\\n<h1>text</h1>\\r\\n}}}\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td\\r\\nThe result will be syntax highlighted HTML code:\\r\\n {{{\\r\\n#!text/html\\r\\n<h1>text</h1>\\r\\n }}}\\r\\n\\r\\nThe same is valid for all other [TracSyntaxColoring#SyntaxColoringSupport mime types supported].\\r\\n}}}\\r\\n}}}\\r\\n{{{#!td\\r\\n {{{\\r\\n{{{\\r\\n#!diff\\r\\n--- Version 55\\r\\n+++ Version 56\\r\\n@@ -115,8 +115,9 @@\\r\\n     name=''TracHelloWorld'', version=''1.0'',\\r\\n     packages=find_packages(exclude=[''*.tests*'']),\\r\\n-    entry_points = \"\"\"\\r\\n-        [trac.plugins]\\r\\n-        helloworld = myplugs.helloworld\\r\\n-    \"\"\",\\r\\n+    entry_points = {\\r\\n+        ''trac.plugins'': [\\r\\n+            ''helloworld = myplugs.helloworld'',\\r\\n+        ],\\r\\n+    },\\r\\n )\\r\\n}}}\\r\\n }}}\\r\\n}}}\\r\\n{{{#!td\\r\\n''''''`#!diff`'''''' has a particularly nice renderer:\\r\\n {{{\\r\\n#!diff\\r\\n--- Version 55\\r\\n+++ Version 56\\r\\n@@ -115,8 +115,9 @@\\r\\n     name=''TracHelloWorld'', version=''1.0'',\\r\\n     packages=find_packages(exclude=[''*.tests*'']),\\r\\n-    entry_points = \"\"\"\\r\\n-        [trac.plugins]\\r\\n-        helloworld = myplugs.helloworld\\r\\n-    \"\"\",\\r\\n+    entry_points = {\\r\\n+        ''trac.plugins'': [\\r\\n+            ''helloworld = myplugs.helloworld'',\\r\\n+        ],\\r\\n+    },\\r\\n )\\r\\n }}}\\r\\n}}}\\r\\n\\r\\nFor more processor macros developed and/or contributed by users, visit: \\r\\n * [trac:ProcessorBazaar]\\r\\n * [trac:MacroBazaar]\\r\\n * [http://trac-hacks.org Trac Hacks] community site\\r\\n\\r\\nDeveloping processors is no different from Wiki macros. \\r\\nIn fact they work the same way, only the usage syntax differs. \\r\\nSee WikiMacros#DevelopingCustomMacros for more information.\\r\\n\\r\\n\\r\\n----\\r\\nSee also: WikiMacros, WikiHtml, WikiRestructuredText, TracSyntaxColoring, WikiFormatting, TracGuide', NULL, NULL);\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('TracWorkflow', 1, 1362994208345414, 'trac', '127.0.0.1', '= The Trac Ticket Workflow System =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nThe Trac issue database provides a configurable workflow.\\r\\n\\r\\n== The Default Ticket Workflow ==\\r\\n=== Environments upgraded from 0.10 ===\\r\\nWhen you run `trac-admin <env> upgrade`, your `trac.ini` will be modified to include a `[ticket-workflow]` section.\\r\\nThe workflow configured in this case is the original workflow, so that ticket actions will behave like they did in 0.10.\\r\\n\\r\\nGraphically, that looks like this:\\r\\n\\r\\n{{{#!Workflow width=500 height=240\\r\\nleave = * -> *\\r\\nleave.operations = leave_status\\r\\nleave.default = 1\\r\\naccept = new -> assigned\\r\\naccept.permissions = TICKET_MODIFY\\r\\naccept.operations = set_owner_to_self\\r\\nresolve = new,assigned,reopened -> closed\\r\\nresolve.permissions = TICKET_MODIFY\\r\\nresolve.operations = set_resolution\\r\\nreassign = new,assigned,reopened -> new\\r\\nreassign.permissions = TICKET_MODIFY\\r\\nreassign.operations = set_owner\\r\\nreopen = closed -> reopened\\r\\nreopen.permissions = TICKET_CREATE\\r\\nreopen.operations = del_resolution\\r\\n}}}\\r\\n\\r\\nThere are some significant \"warts\" in this; such as accepting a ticket sets it to ''assigned'' state, and assigning a ticket sets it to ''new'' state.  Perfectly obvious, right?\\r\\nSo you will probably want to migrate to \"basic\" workflow; [trac:source:trunk/contrib/workflow/migrate_original_to_basic.py contrib/workflow/migrate_original_to_basic.py] may be helpful.\\r\\n\\r\\n=== Environments created with 0.11 ===\\r\\nWhen a new environment is created, a default workflow is configured in your trac.ini.  This workflow is the basic workflow (described in `basic-workflow.ini`), which is somewhat different from the workflow of the 0.10 releases.\\r\\n\\r\\nGraphically, it looks like this:\\r\\n\\r\\n{{{#!Workflow width=700 height=300\\r\\nleave = * -> *\\r\\nleave.operations = leave_status\\r\\nleave.default = 1\\r\\naccept = new,assigned,accepted,reopened -> accepted\\r\\naccept.permissions = TICKET_MODIFY\\r\\naccept.operations = set_owner_to_self\\r\\nresolve = new,assigned,accepted,reopened -> closed\\r\\nresolve.permissions = TICKET_MODIFY\\r\\nresolve.operations = set_resolution\\r\\nreassign = new,assigned,accepted,reopened -> assigned\\r\\nreassign.permissions = TICKET_MODIFY\\r\\nreassign.operations = set_owner\\r\\nreopen = closed -> reopened\\r\\nreopen.permissions = TICKET_CREATE\\r\\nreopen.operations = del_resolution\\r\\n}}}\\r\\n\\r\\n== Additional Ticket Workflows ==\\r\\n\\r\\nThere are several example workflows provided in the Trac source tree; look in [trac:source:trunk/contrib/workflow contrib/workflow] for `.ini` config sections.  One of those may be a good match for what you want. They can be pasted into the `[ticket-workflow]` section of your `trac.ini` file. However if you have existing tickets then there may be issues if those tickets have states that are not in the new workflow. \\r\\n\\r\\nHere are some [http://trac.edgewall.org/wiki/WorkFlow/Examples diagrams] of the above examples.\\r\\n\\r\\n== Basic Ticket Workflow Customization ==\\r\\n\\r\\nNote: Ticket \"statuses\" or \"states\" are not separately defined. The states a ticket can be in are automatically generated by the transitions defined in a workflow. Therefore, creating a new ticket state simply requires defining a state transition in the workflow that starts or ends with that state.\\r\\n\\r\\nCreate a `[ticket-workflow]` section in `trac.ini`.\\r\\nWithin this section, each entry is an action that may be taken on a ticket. \\r\\nFor example, consider the `accept` action from `simple-workflow.ini`:\\r\\n{{{\\r\\naccept = new,accepted -> accepted\\r\\naccept.permissions = TICKET_MODIFY\\r\\naccept.operations = set_owner_to_self\\r\\n}}}\\r\\nThe first line in this example defines the `accept` action, along with the states the action is valid in (`new` and `accepted`), and the new state of the ticket when the action is taken (`accepted`).\\r\\nThe `accept.permissions` line specifies what permissions the user must have to use this action.\\r\\nThe `accept.operations` line specifies changes that will be made to the ticket in addition to the status change when this action is taken.  In this case, when a user clicks on `accept`, the ticket owner field is updated to the logged in user.  Multiple operations may be specified in a comma separated list.\\r\\n\\r\\nThe available operations are:\\r\\n - del_owner -- Clear the owner field.\\r\\n - set_owner -- Sets the owner to the selected or entered owner.\\r\\n   - ''''actionname''''`.set_owner` may optionally be set to a comma delimited list or a single value.\\r\\n - set_owner_to_self -- Sets the owner to the logged in user.\\r\\n - del_resolution -- Clears the resolution field\\r\\n - set_resolution -- Sets the resolution to the selected value.\\r\\n   - ''''actionname''''`.set_resolution` may optionally be set to a comma delimited list or a single value. Example:\\r\\n     {{{\\r\\nresolve_new = new -> closed\\r\\nresolve_new.name = resolve\\r\\nresolve_new.operations = set_resolution\\r\\nresolve_new.permissions = TICKET_MODIFY\\r\\nresolve_new.set_resolution = invalid,wontfix\\r\\n     }}}\\r\\n - leave_status -- Displays \"leave as <current status>\" and makes no change to the ticket.\\r\\n''''''Note:'''''' Specifying conflicting operations (such as `set_owner` and `del_owner`) has unspecified results.\\r\\n\\r\\n{{{\\r\\nresolve_accepted = accepted -> closed\\r\\nresolve_accepted.name = resolve\\r\\nresolve_accepted.permissions = TICKET_MODIFY\\r\\nresolve_accepted.operations = set_resolution\\r\\n}}}\\r\\n\\r\\nIn this example, we see the `.name` attribute used.  The action here is `resolve_accepted`, but it will be presented to the user as `resolve`.\\r\\n\\r\\nFor actions that should be available in all states, `*` may be used in place of the state.  The obvious example is the `leave` action:\\r\\n{{{\\r\\nleave = * -> *\\r\\nleave.operations = leave_status\\r\\nleave.default = 1\\r\\n}}}\\r\\nThis also shows the use of the `.default` attribute.  This value is expected to be an integer, and the order in which the actions are displayed is determined by this value.  The action with the highest `.default` value is listed first, and is selected by default.  The rest of the actions are listed in order of decreasing `.default` values.\\r\\nIf not specified for an action, `.default` is 0.  The value may be negative.\\r\\n\\r\\nThere are a couple of hard-coded constraints to the workflow.  In particular, tickets are created with status `new`, and tickets are expected to have a `closed` state.  Further, the default reports/queries treat any state other than `closed` as an open state.\\r\\n\\r\\nWhile creating or modifying a ticket workflow, `contrib/workflow/workflow_parser.py` may be useful.  It can create `.dot` files that [http://www.graphviz.org GraphViz] understands to provide a visual description of the workflow.\\r\\n\\r\\nThis can be done as follows (your install path may be different).\\r\\n{{{\\r\\ncd /var/local/trac_devel/contrib/workflow/\\r\\nsudo ./showworkflow /srv/trac/PlannerSuite/conf/trac.ini\\r\\n}}}\\r\\nAnd then open up the resulting `trac.pdf` file created by the script (it will be in the same directory as the `trac.ini` file).\\r\\n\\r\\nAn online copy of the workflow parser is available at http://foss.wush.net/cgi-bin/visual-workflow.pl\\r\\n\\r\\nAfter you have changed a workflow, you need to restart apache for the changes to take effect. This is important, because the changes will still show up when you run your script, but all the old workflow steps will still be there until the server is restarted.\\r\\n\\r\\n== Example: Adding optional Testing with Workflow ==\\r\\n\\r\\nBy adding the following to your [ticket-workflow] section of trac.ini you get optional testing.  When the ticket is in new, accepted or needs_work status you can choose to submit it for testing.  When it''s in the testing status the user gets the option to reject it and send it back to needs_work, or pass the testing and send it along to closed.  If they accept it then it gets automatically marked as closed and the resolution is set to fixed.  Since all the old work flow remains, a ticket can skip this entire section.\\r\\n\\r\\n{{{\\r\\ntesting = new,accepted,needs_work,assigned,reopened -> testing\\r\\ntesting.name = Submit to reporter for testing\\r\\ntesting.permissions = TICKET_MODIFY\\r\\n\\r\\nreject = testing -> needs_work\\r\\nreject.name = Failed testing, return to developer\\r\\n\\r\\npass = testing -> closed\\r\\npass.name = Passes Testing\\r\\npass.operations = set_resolution\\r\\npass.set_resolution = fixed\\r\\n}}}\\r\\n\\r\\n=== How to combine the `tracopt.ticket.commit_updater` with the testing workflow ===\\r\\n\\r\\nThe [[trac:source:trunk/tracopt/ticket/commit_updater.py|tracopt.ticket.commit_updater]] is the optional component that [[TracRepositoryAdmin#trac-post-commit-hook|replaces the old trac-post-commit-hook]], in Trac 0.12.\\r\\n\\r\\nBy default it reacts on some keywords found in changeset message logs like ''''close'''', ''''fix'''' etc. and performs the corresponding workflow action.\\r\\n\\r\\nIf you have a more complex workflow, like the testing stage described above and you want the ''''closes'''' keyword to move the ticket to the ''''testing'''' status instead of the ''''closed'''' status, you need to adapt the code a bit. \\r\\n\\r\\nHave a look at the [[trac:wiki:0.11/TracWorkflow#How-ToCombineSVNtrac-post-commit-hookWithTestWorkflow|Trac 0.11 recipe]] for the `trac-post-commit-hook`, this will give you some ideas about how to modify the component.\\r\\n\\r\\n== Example: Add simple optional generic review state ==\\r\\n\\r\\nSometimes Trac is used in situations where \"testing\" can mean different things to different people so you may want to create an optional workflow state that is between the default workflow''s `assigned` and `closed` states, but does not impose implementation-specific details. The only new state you need to add for this is a `reviewing` state. A ticket may then be \"submitted for review\" from any state that it can be reassigned. If a review passes, you can re-use the `resolve` action to close the ticket, and if it fails you can re-use the `reassign` action to push it back into the normal workflow.\\r\\n\\r\\nThe new `reviewing` state along with its associated `review` action looks like this:\\r\\n\\r\\n{{{\\r\\nreview = new,assigned,reopened -> reviewing\\r\\nreview.operations = set_owner\\r\\nreview.permissions = TICKET_MODIFY\\r\\n}}}\\r\\n\\r\\nThen, to integrate this with the default Trac 0.11 workflow, you also need to add the `reviewing` state to the `accept` and `resolve` actions, like so:\\r\\n\\r\\n{{{\\r\\naccept = new,reviewing -> assigned\\r\\n[…]\\r\\nresolve = new,assigned,reopened,reviewing -> closed\\r\\n}}}\\r\\n\\r\\nOptionally, you can also add a new action that allows you to change the ticket''s owner without moving the ticket out of the `reviewing` state. This enables you to reassign review work without pushing the ticket back to the `new` status.\\r\\n\\r\\n{{{\\r\\nreassign_reviewing = reviewing -> *\\r\\nreassign_reviewing.name = reassign review\\r\\nreassign_reviewing.operations = set_owner\\r\\nreassign_reviewing.permissions = TICKET_MODIFY\\r\\n}}}\\r\\n\\r\\nThe full `[ticket-workflow]` configuration will thus look like this:\\r\\n\\r\\n{{{\\r\\n[ticket-workflow]\\r\\naccept = new,reviewing -> assigned\\r\\naccept.operations = set_owner_to_self\\r\\naccept.permissions = TICKET_MODIFY\\r\\nleave = * -> *\\r\\nleave.default = 1\\r\\nleave.operations = leave_status\\r\\nreassign = new,assigned,accepted,reopened -> assigned\\r\\nreassign.operations = set_owner\\r\\nreassign.permissions = TICKET_MODIFY\\r\\nreopen = closed -> reopened\\r\\nreopen.operations = del_resolution\\r\\nreopen.permissions = TICKET_CREATE\\r\\nresolve = new,assigned,reopened,reviewing -> closed\\r\\nresolve.operations = set_resolution\\r\\nresolve.permissions = TICKET_MODIFY\\r\\nreview = new,assigned,reopened -> reviewing\\r\\nreview.operations = set_owner\\r\\nreview.permissions = TICKET_MODIFY\\r\\nreassign_reviewing = reviewing -> *\\r\\nreassign_reviewing.operations = set_owner\\r\\nreassign_reviewing.name = reassign review\\r\\nreassign_reviewing.permissions = TICKET_MODIFY\\r\\n}}}\\r\\n\\r\\n== Example: Limit the resolution options for a new ticket ==\\r\\n\\r\\nThe above resolve_new operation allows you to set the possible resolutions for a new ticket.  By modifying the existing resolve action and removing the new status from before the `->` we then get two resolve actions.  One with limited resolutions for new tickets, and then the regular one once a ticket is accepted.\\r\\n\\r\\n{{{\\r\\nresolve_new = new -> closed\\r\\nresolve_new.name = resolve\\r\\nresolve_new.operations = set_resolution\\r\\nresolve_new.permissions = TICKET_MODIFY\\r\\nresolve_new.set_resolution = invalid,wontfix,duplicate\\r\\n\\r\\nresolve = assigned,accepted,reopened -> closed\\r\\nresolve.operations = set_resolution\\r\\nresolve.permissions = TICKET_MODIFY\\r\\n}}}\\r\\n\\r\\n== Advanced Ticket Workflow Customization ==\\r\\n\\r\\nIf the customization above is not extensive enough for your needs, you can extend the workflow using plugins.  These plugins can provide additional operations for the workflow (like code_review), or implement side-effects for an action (such as triggering a build) that may not be merely simple state changes.  Look at [trac:source:trunk/sample-plugins/workflow sample-plugins/workflow] for a few simple examples to get started.\\r\\n\\r\\nBut if even that is not enough, you can disable the !ConfigurableTicketWorkflow component and create a plugin that completely replaces it.\\r\\n\\r\\n== Adding Workflow States to Milestone Progress Bars ==\\r\\n\\r\\nIf you add additional states to your workflow, you may want to customize your milestone progress bars as well.  See [TracIni#milestone-groups-section TracIni].\\r\\n\\r\\n== some ideas for next steps ==\\r\\n\\r\\nNew enhancement ideas for the workflow system should be filed as enhancement tickets against the `ticket system` component.  If desired, add a single-line link to that ticket here.  Also look at the [http://trac-hacks.org/wiki/AdvancedTicketWorkflowPlugin AdvancedTicketWorkflowPlugin] as it provides experimental operations.\\r\\n\\r\\nIf you have a response to the comments below, create an enhancement ticket, and replace the description below with a link to the ticket.\\r\\n\\r\\n * the \"operation\" could be on the nodes, possible operations are:\\r\\n   * ''''''preops'''''': automatic, before entering the state/activity\\r\\n   * ''''''postops'''''': automatic, when leaving the state/activity\\r\\n   * ''''''actions'''''': can be chosen by the owner in the list at the bottom, and/or drop-down/pop-up together with the default actions of leaving the node on one of the arrows.\\r\\n''''This appears to add complexity without adding functionality; please provide a detailed example where these additions allow something currently impossible to implement.''''\\r\\n\\r\\n * operations could be anything: sum up the time used for the activity, or just write some statistical fields like \\r\\n''''A workflow plugin can add an arbitrary workflow operation, so this is already possible.''''\\r\\n\\r\\n * set_actor should be an operation allowing to set the owner, e.g. as a \"preop\":\\r\\n   * either to a role, a person\\r\\n   * entered fix at define time, or at run time, e.g. out of a field, or select.\\r\\n''''This is either duplicating the existing `set_owner` operation, or needs to be clarified.''''\\r\\n\\r\\n * Actions should be selectable based on the ticket type (different Workflows for different tickets)\\r\\n''''Look into the [http://trac-hacks.org/wiki/AdvancedTicketWorkflowPlugin AdvancedTicketWorkflowPlugin]''s `triage` operation.''''\\r\\n\\r\\n * I''d wish to have an option to perform automatic status changes. In my case, I do not want to start with \"new\", but with \"assigned\". So tickets in state \"new\" should automatically go into state \"assigned\". Or is there already a way to do this and I just missed it?\\r\\n''''Have a look at [http://trac-hacks.org/wiki/TicketCreationStatusPlugin TicketCreationStatusPlugin] and [http://trac-hacks.org/wiki/TicketConditionalCreationStatusPlugin TicketConditionalCreationStatusPlugin]''''\\r\\n\\r\\n * I added a ''testing'' state. A tester can close the ticket or reject it. I''d like the transition from testing to rejected to set the owner to the person that put the ticket in ''testing''. The [http://trac-hacks.org/wiki/AdvancedTicketWorkflowPlugin AdvancedTicketWorkflowPlugin] is close with set_owner_to_field, but we need something like set_field_to_owner.\\r\\n\\r\\n * I''d like to track the time a ticket is in each state, adding up ''disjoints'' intervals in the same state.\\r\\n', NULL, NULL),\n('TracGuide', 1, 1362994208347378, 'trac', '127.0.0.1', '= The Trac User and Administration Guide =\\r\\n[[TracGuideToc]]\\r\\n{{{#!span style=\"font-size:90%\"\\r\\n//The TracGuide is meant to serve as a starting point for all documentation regarding Trac usage and development. The guide is a free document, a collaborative effort, and a part of the [http://trac.edgewall.org Trac Project] itself.//\\r\\n}}}\\r\\n\\r\\n== Introduction\\r\\n\\r\\nTrac is an enhanced wiki and issue tracking system for software development projects. Trac uses a minimalistic approach to web-based software project management. It strives to help developers write great software while staying out of the way. Trac should impose as little as possible on a team''s established development process and policies.\\r\\n\\r\\nIt provides an interface to Subversion (and other version control systems), an integrated Wiki and convenient reporting facilities.\\r\\n\\r\\nTrac allows wiki markup in issue descriptions and commit messages, creating links and seamless references between bugs, tasks, changesets, files and wiki pages. A timeline shows all current and past project events in order, making the acquisition of an overview of the project and tracking progress very easy. The roadmap shows the road ahead, listing the upcoming milestones.\\r\\n== User Guide\\r\\n   * Using the Wiki subsystem\\r\\n     * TracWiki — How to use the built-in Wiki.\\r\\n     * WikiFormatting — Reference to the wiki syntax used throughout.\\r\\n   * Using the Version Control subsystem\\r\\n     * TracBrowser — Browsing source code with Trac.\\r\\n     * TracChangeset — Viewing changes to source code.\\r\\n     * TracRevisionLog — Viewing change history.\\r\\n   * Using the Ticket subsystem\\r\\n     * TracTickets — Using the issue tracker.\\r\\n     * TracRoadmap — The roadmap helps tracking project progress.\\r\\n     * TracReports — Writing and using reports.\\r\\n     * TracQuery — Executing custom ticket queries.\\r\\n     * TracBatchModify - Modifying a batch of tickets in one request.\\r\\n   * Other modules and general topics\\r\\n     * TracSearch — Full text search in all content.\\r\\n     * TracTimeline — The timeline provides a historic perspective on a project.\\r\\n     * TracRss — RSS content syndication in Trac.\\r\\n     * TracAccessibility — Accessibility keys support\\r\\n\\r\\n\\r\\n== Administrator Guide\\r\\n   * Installation and upgrade\\r\\n     * TracInstall — How to install and run Trac.\\r\\n     * TracUpgrade — How to upgrade existing installations.\\r\\n     * TracImport — Importing tickets from other bug databases.\\r\\n     * TracPlugins — Installing and managing Trac extensions.\\r\\n   * Configuration and customization\\r\\n     * TracIni — Trac configuration file reference. \\r\\n     * TracPermissions — Access control and permissions.\\r\\n     * TracNavigation — Customize main navigation menus.\\r\\n     * TracInterfaceCustomization — Customizing the Trac interface.\\r\\n     * TracLogging — The Trac logging facility.\\r\\n   * Administering the Version Control subsystem\\r\\n     * TracRepositoryAdmin — Management of Source Code Repositories.\\r\\n   * Administering the Ticket subsystem\\r\\n     * TracTicketsCustomFields — Expanding tickets with customized fields.\\r\\n     * TracNotification — Email notification.\\r\\n     * TracWorkflow — Configurable Ticket Workflow.\\r\\n   * Reference\\r\\n     * TracEnvironment — All you need to know about Trac environments\\r\\n     * TracAdmin — Administering a Trac project via the command-line.\\r\\n\\r\\n== Support and Other Sources of Information ==\\r\\n\\r\\n * [trac:TracFaq Trac FAQ] — A collection of Frequently Asked Questions (on the project website).\\r\\n * [trac:TracDev] and [trac:TracDev/ApiDocs API docs] — Trac Developer documentation\\r\\n * TracSupport — How to get more information\\r\\n\\r\\nIf you are looking for a good place to ask a question about Trac, look no further than the [http://trac.edgewall.org/wiki/MailingList MailingList]. It provides a friendly environment to discuss openly among Trac users and developers.\\r\\n', NULL, NULL),\n('TracInstall', 1, 1362994208348711, 'trac', '127.0.0.1', '= Trac Installation Guide for 1.0 = \\r\\n[[TracGuideToc]]\\r\\n\\r\\nTrac is written in the Python programming language and needs a database, [http://sqlite.org/ SQLite], [http://www.postgresql.org/ PostgreSQL], or [http://mysql.com/ MySQL]. For HTML rendering, Trac uses the [http://genshi.edgewall.org Genshi] templating system.\\r\\n\\r\\nSince version 0.12, Trac can also be localized, and there''s probably a translation available for your language. If you want to be able to use the Trac interface in other languages, then make sure you have installed the optional package [#OtherPythonPackages Babel]. Pay attention to the extra steps for localization support in the [#InstallingTrac Installing Trac] section below. Lacking Babel, you will only get the default english version, as usual.\\r\\n\\r\\nIf you''re interested in contributing new translations for other languages or enhance the existing translations, then please have a look at [[trac:TracL10N]].\\r\\n\\r\\nWhat follows are generic instructions for installing and setting up Trac and its requirements. While you may find instructions for installing Trac on specific systems at [trac:TracInstallPlatforms TracInstallPlatforms] on the main Trac site, please be sure to ''''''first read through these general instructions'''''' to get a good understanding of the tasks involved.\\r\\n\\r\\n[[PageOutline(2-3,Installation Steps,inline)]]\\r\\n\\r\\n== Dependencies ==\\r\\n=== Mandatory Dependencies\\r\\nTo install Trac, the following software packages must be installed:\\r\\n\\r\\n * [http://www.python.org/ Python], version >= 2.5 and < 3.0\\r\\n   (note that we dropped the support for Python 2.4 in this release)\\r\\n * [http://peak.telecommunity.com/DevCenter/setuptools setuptools], version >= 0.6, or better yet, [http://pypi.python.org/pypi/distribute distribute]\\r\\n * [http://genshi.edgewall.org/wiki/Download Genshi], version >= 0.6 (unreleased version 0.7dev should work as well)\\r\\n\\r\\nYou also need a database system and the corresponding python bindings.\\r\\nThe database can be either SQLite, PostgreSQL or MySQL.\\r\\n\\r\\n==== For the SQLite database #ForSQLite\\r\\n\\r\\nAs you must be using Python 2.5, 2.6 or 2.7, you already have the SQLite database bindings bundled with the standard distribution of Python (the `sqlite3` module).\\r\\n\\r\\nHowever, if you''d like, you can download the latest and greatest version of [[trac:Pysqlite]] from \\r\\n[http://code.google.com/p/pysqlite/downloads/list google code], where you''ll find the Windows\\r\\ninstallers or the `tar.gz` archive for building from source: \\r\\n{{{\\r\\n$ tar xvfz <version>.tar.gz \\r\\n$ cd <version> \\r\\n$ python setup.py build_static install \\r\\n}}}\\r\\n \\r\\nThis will download the latest SQLite code and build the bindings. \\r\\n\\r\\nSQLite 2.x is no longer supported.\\r\\n\\r\\nA known bug PySqlite versions 2.5.2-4 prohibits upgrade of trac databases\\r\\nfrom 0.11.x to 0.12. Please use versions 2.5.5 and newer or 2.5.1 and\\r\\nolder. See #9434 for more detail.\\r\\n\\r\\nSee additional information in [trac:PySqlite PySqlite].\\r\\n\\r\\n==== For the PostgreSQL database #ForPostgreSQL\\r\\n\\r\\nYou need to install the database and its Python bindings:\\r\\n * [http://www.postgresql.org/ PostgreSQL], version 8.0 or later\\r\\n * [http://pypi.python.org/pypi/psycopg2 psycopg2]\\r\\n\\r\\nSee [trac:DatabaseBackend#Postgresql DatabaseBackend] for details.\\r\\n\\r\\n\\r\\n==== For the MySQL database #ForMySQL\\r\\n\\r\\nTrac can now work quite well with MySQL, provided you follow the guidelines.\\r\\n\\r\\n * [http://mysql.com/ MySQL], version 5.0 or later\\r\\n * [http://sf.net/projects/mysql-python MySQLdb], version 1.2.2 or later\\r\\n\\r\\nIt is ''''''very'''''' important to read carefully the  [trac:MySqlDb] page before creating the database.\\r\\n\\r\\n=== Optional Dependencies\\r\\n\\r\\n==== Version Control System ====\\r\\n\\r\\n===== Subversion =====\\r\\n * [http://subversion.apache.org/ Subversion], 1.5.x or 1.6.x and the ''''''''''corresponding'''''''''' Python bindings. Older versions starting from 1.0, like 1.2.4, 1.3.2 or 1.4.2, etc. should still work. For troubleshooting information, check the [trac:TracSubversion#Troubleshooting TracSubversion] page.\\r\\n\\r\\nThere are [http://subversion.apache.org/packages.html pre-compiled SWIG bindings] available for various platforms. (Good luck finding precompiled SWIG bindings for any Windows package at that listing. TracSubversion points you to [http://alagazam.net Algazam], which works for me under Python 2.6.)\\r\\n\\r\\nNote that Trac ''''''doesn''t'''''' use [http://pysvn.tigris.org/ PySVN], neither does it work yet with the newer `ctype`-style bindings. \\r\\n\\r\\n\\r\\n''''''Please note:'''''' if using Subversion, Trac must be installed on the ''''''same machine''''''. Remote repositories are currently [trac:ticket:493 not supported].\\r\\n\\r\\n\\r\\n===== Others =====\\r\\n\\r\\nSupport for other version control systems is provided via third-parties. See [trac:PluginList] and [trac:VersionControlSystem].\\r\\n\\r\\n==== Web Server ====\\r\\nA web server is optional because Trac is shipped with a server included, see the [#RunningtheStandaloneServer Running the Standalone Server ] section below.\\r\\n\\r\\nAlternatively you configure Trac to run in any of the following environments.\\r\\n * [http://httpd.apache.org/ Apache] with \\r\\n   - [http://code.google.com/p/modwsgi/ mod_wsgi], see [wiki:TracModWSGI] and \\r\\n     http://code.google.com/p/modwsgi/wiki/IntegrationWithTrac\\r\\n   - [http://modpython.org/ mod_python 3.3.1], deprecated: see TracModPython)\\r\\n * a [http://www.fastcgi.com/ FastCGI]-capable web server (see TracFastCgi)\\r\\n * an [http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html AJP]-capable web\\r\\n   server (see [trac:TracOnWindowsIisAjp TracOnWindowsIisAjp])\\r\\n * a CGI-capable web server (see TracCgi), ''''''but usage of Trac as a cgi script \\r\\n   is highly discouraged'''''', better use one of the previous options. \\r\\n   \\r\\n\\r\\n==== Other Python Packages ====\\r\\n\\r\\n * [http://babel.edgewall.org Babel], version >= 0.9.5, \\r\\n   needed for localization support (unreleased version 1.0dev should work as well)\\r\\n * [http://docutils.sourceforge.net/ docutils], version >= 0.3.9 \\r\\n   for WikiRestructuredText.\\r\\n * [http://pygments.pocoo.org Pygments] for \\r\\n   [wiki:TracSyntaxColoring syntax highlighting].\\r\\n   [http://silvercity.sourceforge.net/ SilverCity] and/or \\r\\n   [http://gnu.org/software/enscript/enscript.html Enscript] may still be used\\r\\n   but are deprecated and you really should be using Pygments.\\r\\n * [http://pytz.sf.net pytz] to get a complete list of time zones,\\r\\n   otherwise Trac will fall back on a shorter list from \\r\\n   an internal time zone implementation.\\r\\n\\r\\n''''''Attention'''''': The various available versions of these dependencies are not necessarily interchangable, so please pay attention to the version numbers above. If you are having trouble getting Trac to work please double-check all the dependencies before asking for help on the [trac:MailingList] or [trac:IrcChannel].\\r\\n\\r\\nPlease refer to the documentation of these packages to find out how they are best installed. In addition, most of the [trac:TracInstallPlatforms platform-specific instructions] also describe the installation of the dependencies. Keep in mind however that the information there ''''probably concern older versions of Trac than the one you''re installing'''' (there are even some pages that are still talking about Trac 0.8!).\\r\\n\\r\\n\\r\\n== Installing Trac ==\\r\\n=== Using `easy_install`\\r\\nOne way to install Trac is using [http://pypi.python.org/pypi/setuptools setuptools].\\r\\nWith setuptools you can install Trac from the subversion repository; \\r\\n\\r\\nA few examples:\\r\\n\\r\\n - install Trac 1.0:\\r\\n   {{{\\r\\n   easy_install Trac==1.0\\r\\n   }}}\\r\\n   (NOT YET ENABLED)\\r\\n - install latest development version 1.0dev:\\r\\n   {{{\\r\\n   easy_install Trac==dev\\r\\n   }}}\\r\\n   Note that in this case you won''t have the possibility to run a localized version of Trac;\\r\\n   either use a released version or install from source \\r\\n\\r\\n=== Using `pip`\\r\\n''pip'' is an easy_install replacement that is very useful to quickly install python packages.\\r\\nTo get a trac installation up and running in less than 5 minutes:\\r\\n\\r\\nAssuming you want to have your entire pip installation in `/opt/user/trac`\\r\\n\\r\\n - \\r\\n{{{\\r\\npip -E /opt/user/trac install trac psycopg2 \\r\\n}}}\\r\\nor\\r\\n - \\r\\n{{{\\r\\npip -E /opt/user/trac install trac mysql-python \\r\\n}}}\\r\\n\\r\\nMake sure your OS specific headers are available for pip to automatically build PostgreSQL (libpq-dev) or MySQL (libmysqlclient-dev) bindings.\\r\\n\\r\\npip will automatically resolve all dependencies (like Genshi, pygments, etc.) and download the latest packages on pypi.python.org and create a self contained installation in `/opt/user/trac`.\\r\\n\\r\\nAll commands (`tracd`, `trac-admin`) are available in `/opt/user/trac/bin`. This can also be leveraged for `mod_python` (using `PythonHandler` directive) and `mod_wsgi` (using `WSGIDaemonProcess` directive)\\r\\n\\r\\nAdditionally, you can install several trac plugins (listed [http://pypi.python.org/pypi?:action=search&term=trac&submit=search here]) through pip.\\r\\n\\r\\n\\r\\n\\r\\n=== From source\\r\\nOf course, using the python-typical setup at the top of the source directory also works.\\r\\n\\r\\nYou can obtain the source for a .tar.gz or .zip file corresponding to a release (e.g. Trac-1.0.tar.gz), or you can get the source directly from the repository (see Trac:SubversionRepository for details).\\r\\n\\r\\n{{{\\r\\n$ python ./setup.py install\\r\\n}}}\\r\\n\\r\\n''''You''ll need root permissions or equivalent for this step.''''\\r\\n\\r\\nThis will byte-compile the python source code and install it as an .egg file or folder in the `site-packages` directory\\r\\nof your Python installation. The .egg will also contain all other resources needed by standard Trac, such as htdocs and templates.\\r\\n\\r\\nThe script will also install the [wiki:TracAdmin trac-admin] command-line tool, used to create and maintain [wiki:TracEnvironment project environments], as well as the [wiki:TracStandalone tracd] standalone server.\\r\\n\\r\\nIf you install from source and want to make Trac available in other languages, make sure  Babel is installed. Only then, perform the `install` (or simply redo the `install` once again afterwards if you realize Babel was not yet installed):\\r\\n{{{\\r\\n$ python ./setup.py install\\r\\n}}}\\r\\nAlternatively, you can do a `bdist_egg` and copy the .egg from dist/ to the place of your choice, or you can create a Windows installer (`bdist_wininst`).\\r\\n\\r\\n=== Advanced Options ===\\r\\n\\r\\nTo install Trac to a custom location, or find out about other advanced installation options, run:\\r\\n{{{\\r\\neasy_install --help\\r\\n}}}\\r\\n\\r\\nAlso see [http://docs.python.org/inst/inst.html Installing Python Modules] for detailed information.\\r\\n\\r\\nSpecifically, you might be interested in:\\r\\n{{{\\r\\neasy_install --prefix=/path/to/installdir\\r\\n}}}\\r\\nor, if installing Trac to a Mac OS X system:\\r\\n{{{\\r\\neasy_install --prefix=/usr/local --install-dir=/Library/Python/2.5/site-packages\\r\\n}}}\\r\\nNote: If installing on Mac OS X 10.6 running {{{ easy_install http://svn.edgewall.org/repos/trac/trunk }}} will install into {{{ /usr/local }}} and {{{ /Library/Python/2.6/site-packages }}} by default\\r\\n\\r\\nThe above will place your `tracd` and `trac-admin` commands into `/usr/local/bin` and will install the Trac libraries and dependencies into `/Library/Python/2.5/site-packages`, which is Apple''s preferred location for third-party Python application installations.\\r\\n\\r\\n\\r\\n== Creating a Project Environment ==\\r\\n\\r\\nA [TracEnvironment Trac environment] is the backend storage where Trac stores information like wiki pages, tickets, reports, settings, etc. An environment is basically a directory that contains a human-readable [TracIni configuration file], and various other files and directories.\\r\\n\\r\\nA new environment is created using [wiki:TracAdmin trac-admin]:\\r\\n{{{\\r\\n$ trac-admin /path/to/myproject initenv\\r\\n}}}\\r\\n\\r\\n[TracAdmin trac-admin] will prompt you for the information it needs to create the environment, such as the name of the project and the [TracEnvironment#DatabaseConnectionStrings database connection string]. If you''re not sure what to specify for one of these options, just press `<Enter>` to use the default value. \\r\\n\\r\\nUsing the default database connection string in particular will always work as long as you have SQLite installed.\\r\\nFor the other [DatabaseBackend database backends] you should plan ahead and already have a database ready to use at this point.\\r\\n\\r\\nSince 0.12, Trac doesn''t ask for a [TracEnvironment#SourceCodeRepository source code repository] anymore when creating an environment. Repositories can be [TracRepositoryAdmin added] afterward, or the version control support can be disabled completely if you don''t need it.\\r\\n\\r\\nAlso note that the values you specify here can be changed later by directly editing the [TracIni conf/trac.ini] configuration file.\\r\\n\\r\\nFinally, make sure the user account under which the web front-end runs will have ''''''write permissions'''''' to the environment directory and all the files inside. This will be the case if you run `trac-admin ... initenv` as this user. If not, you should set the correct user afterwards. For example on Linux, with the web server running as user `apache` and group `apache`, enter:\\r\\n{{{\\r\\n# chown -R apache.apache /path/to/myproject\\r\\n}}}\\r\\n\\r\\n{{{#!div class=important\\r\\n''''''Warning:'''''' Please only use ASCII-characters for account name and project path, unicode characters are not supported there.\\r\\n}}}\\r\\n\\r\\n\\r\\n== Deploying Trac\\r\\n\\r\\n=== Running the Standalone Server ===\\r\\n\\r\\nAfter having created a Trac environment, you can easily try the web interface by running the standalone server [wiki:TracStandalone tracd]:\\r\\n{{{\\r\\n$ tracd --port 8000 /path/to/myproject\\r\\n}}}\\r\\n\\r\\nThen, fire up a browser and visit `http://localhost:8000/`. You should get a simple listing of all environments that `tracd` knows about. Follow the link to the environment you just created, and you should see Trac in action. If you only plan on managing a single project with Trac you can have the standalone server skip the environment list by starting it like this:\\r\\n{{{\\r\\n$ tracd -s --port 8000 /path/to/myproject\\r\\n}}}\\r\\n\\r\\n=== Running Trac on a Web Server ===\\r\\n\\r\\nTrac provides various options for connecting to a \"real\" web server: \\r\\n - [wiki:TracFastCgi FastCGI]\\r\\n - [wiki:TracModWSGI mod_wsgi] \\r\\n - //[wiki:TracModPython mod_python] (no longer recommended, as mod_python is not actively maintained anymore)//\\r\\n - //[wiki:TracCgi CGI] (should not be used, as the performance is far from optimal)//\\r\\n\\r\\nTrac also supports [trac:TracOnWindowsIisAjp AJP] which may be your choice if you want to connect to IIS. Other deployment scenarios are possible: [trac:TracNginxRecipe nginx], [http://projects.unbit.it/uwsgi/wiki/Example#Traconapacheinasub-uri uwsgi], [trac:TracOnWindowsIisIsapi Isapi-wsgi] etc.\\r\\n\\r\\n==== Generating the Trac cgi-bin directory ==== #cgi-bin\\r\\n\\r\\nIn order for Trac to function properly with FastCGI you need to have a `trac.fcgi` file and for mod_wsgi a `trac.wsgi` file. These are Python scripts which load the appropriate Python code. They can be generated using the `deploy` option of [wiki:TracAdmin trac-admin].\\r\\n\\r\\nThere is, however, a bit of a chicken-and-egg problem. The [wiki:TracAdmin trac-admin] command requires an existing environment to function, but complains if the deploy directory already exists. This is a problem, because environments are often stored in a subdirectory of the deploy. The solution is to do something like this:\\r\\n{{{\\r\\nmkdir -p /usr/share/trac/projects/my-project\\r\\ntrac-admin /usr/share/trac/projects/my-project initenv\\r\\ntrac-admin /usr/share/trac/projects/my-project deploy /tmp/deploy\\r\\nmv /tmp/deploy/* /usr/share/trac\\r\\n}}}\\r\\n\\r\\n\\r\\n==== Mapping Static Resources ====\\r\\n\\r\\nOut of the box, Trac will pass static resources such as style sheets or images through itself. For anything but a tracd only based deployment, this is far from optimal as the web server could be set up to directly serve those static resources (for CGI setup, this is ''''''highly undesirable'''''' and will cause abysmal performance).\\r\\n\\r\\nWeb servers such as [http://httpd.apache.org/ Apache] allow you to create “Aliases” to resources, giving them a virtual URL that doesn''t necessarily reflect the layout of the servers file system. We also can map requests for static resources directly to the directory on the file system, avoiding processing these requests by Trac itself.\\r\\n\\r\\nThere are two primary URL paths for static resources - `/chrome/common` and `/chrome/site`. Plugins can add their own resources, usually accessible by `/chrome/<plugin>` path, so its important to override only known paths and not try to make universal `/chrome` alias for everything.\\r\\n\\r\\nNote that in order to get those static resources on the filesystem, you need first to extract the relevant resources from Trac using the [TracAdmin trac-admin]` <environment> deploy` command:\\r\\n[[TracAdminHelp(deploy)]]\\r\\n\\r\\nThe target `<directory>` will then contain an `htdocs` directory with:\\r\\n - `site/` - a copy of the environment''s directory `htdocs/` \\r\\n - `common/` - the static resources of Trac itself\\r\\n - `<plugins>/` - one directory for each resource directory managed by the plugins enabled for this environment\\r\\n\\r\\n===== Example: Apache and `ScriptAlias` ===== #ScriptAlias-example\\r\\n\\r\\nAssuming the deployment has been done this way:\\r\\n{{{\\r\\n$ trac-admin /var/trac/env deploy /path/to/trac/htdocs/common\\r\\n}}}\\r\\n\\r\\nAdd the following snippet to Apache configuration ''''before'''' the `ScriptAlias` or `WSGIScriptAlias` (which map all the other requests to the Trac application), changing paths to match your deployment:\\r\\n{{{\\r\\nAlias /trac/chrome/common /path/to/trac/htdocs/common\\r\\nAlias /trac/chrome/site /path/to/trac/htdocs/site\\r\\n\\r\\n<Directory \"/path/to/www/trac/htdocs\">\\r\\n  Order allow,deny\\r\\n  Allow from all\\r\\n</Directory>\\r\\n}}}\\r\\n\\r\\nIf using mod_python, you might want to add this too (otherwise, the alias will be ignored):\\r\\n{{{\\r\\n<Location \"/trac/chrome/common/\">\\r\\n  SetHandler None\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nNote that we mapped `/trac` part of the URL to the `trac.*cgi` script, and the path `/trac/chrome/common` is the path you have to append to that location to intercept requests to the static resources. \\r\\n\\r\\nSimilarly, if you have static resources in a project''s `htdocs` directory (which is referenced by `/trac/chrome/site` URL in themes), you can configure Apache to serve those resources (again, put this ''''before'''' the `ScriptAlias` or `WSGIScriptAlias` for the .*cgi scripts, and adjust names and locations to match your installation):\\r\\n{{{\\r\\nAlias /trac/chrome/site /path/to/projectenv/htdocs\\r\\n\\r\\n<Directory \"/path/to/projectenv/htdocs\">\\r\\n  Order allow,deny\\r\\n  Allow from all\\r\\n</Directory>\\r\\n}}}\\r\\n\\r\\nAlternatively to aliasing `/trac/chrome/common`, you can tell Trac to generate direct links for those static resources (and only those), using the [[wiki:TracIni#trac-section| [trac] htdocs_location]] configuration setting:\\r\\n{{{\\r\\n[trac]\\r\\nhtdocs_location = http://static.example.org/trac-common/\\r\\n}}}\\r\\nNote that this makes it easy to have a dedicated domain serve those static resources (preferentially [http://code.google.com/speed/page-speed/docs/request.html#ServeFromCookielessDomain cookie-less]).\\r\\n\\r\\nOf course, you still need to make the Trac `htdocs/common` directory available through the web server at the specified URL, for example by copying (or linking) the directory into the document root of the web server:\\r\\n{{{\\r\\n$ ln -s /path/to/trac/htdocs/common /var/www/static.example.org/trac-common\\r\\n}}}\\r\\n\\r\\n\\r\\n==== Setting up the Plugin Cache ====\\r\\n\\r\\nSome Python plugins need to be extracted to a cache directory. By default the cache resides in the home directory of the current user. When running Trac on a Web Server as a dedicated user (which is highly recommended) who has no home directory, this might prevent the plugins from starting. To override the cache location you can set the PYTHON_EGG_CACHE environment variable. Refer to your server documentation for detailed instructions on how to set environment variables.\\r\\n\\r\\n== Configuring Authentication ==\\r\\n\\r\\nTrac uses HTTP authentication. You''ll need to configure your webserver to request authentication when the `.../login` URL is hit (the virtual path of the \"login\" button). Trac will automatically pick the REMOTE_USER variable up after you provide your credentials. Therefore, all user management goes through your web server configuration. Please consult the documentation of your web server for more info.\\r\\n\\r\\nThe process of adding, removing, and configuring user accounts for authentication depends on the specific way you run Trac. \\r\\n\\r\\nPlease refer to one of the following sections:\\r\\n * TracStandalone#UsingAuthentication if you use the standalone server, `tracd`.\\r\\n * [wiki:TracModWSGI#ConfiguringAuthentication TracModWSGI#ConfiguringAuthentication] if you use the Apache web server, with any of its front end: `mod_wsgi` of course, but the same instructions applies also for `mod_python`, `mod_fcgi` or `mod_fastcgi`.\\r\\n * TracFastCgi if you''re using another web server with FCGI support (Cherokee, Lighttpd, !LiteSpeed, nginx)\\r\\n\\r\\n== Granting admin rights to the admin user\\r\\nGrant admin rights to user admin:\\r\\n{{{\\r\\n$ trac-admin /path/to/myproject permission add admin TRAC_ADMIN\\r\\n}}}\\r\\nThis user will have an \"Admin\" entry menu that will allow you to admin your trac project.\\r\\n\\r\\n== Finishing the install\\r\\n\\r\\n=== Automatic reference to the SVN changesets in Trac tickets ===\\r\\n\\r\\nYou can configure SVN to automatically add a reference to the changeset into the ticket comments, whenever changes are committed to the repository. The description of the commit needs to contain one of the following formulas:\\r\\n * ''''''`Refs #123`'''''' - to reference this changeset in `#123` ticket\\r\\n * ''''''`Fixes #123`'''''' - to reference this changeset and close `#123` ticket with the default status ''''fixed''''\\r\\n\\r\\nThis functionality requires a post-commit hook to be installed as described in [wiki:TracRepositoryAdmin#ExplicitSync TracRepositoryAdmin], and enabling the optional commit updater components by adding the following line to the `[components]` section of your [wiki:TracIni#components-section trac.ini], or enabling the components in the \"Plugins\" admin panel.\\r\\n{{{\\r\\ntracopt.ticket.commit_updater.* = enabled\\r\\n}}}\\r\\nFor more information, see the documentation of the `CommitTicketUpdater` component in the \"Plugins\" admin panel.\\r\\n\\r\\n=== Using Trac ===\\r\\n\\r\\nOnce you have your Trac site up and running, you should be able to create tickets, view the timeline, browse your version control repository if configured, etc.\\r\\n\\r\\nKeep in mind that //anonymous// (not logged in) users can by default access only a few of the features, in particular they will have a read-only access to the resources. You will need to configure authentication and grant additional [wiki:TracPermissions permissions] to authenticated users to see the full set of features.\\r\\n\\r\\n'''' Enjoy! ''''\\r\\n\\r\\n[trac:TracTeam The Trac Team]\\r\\n\\r\\n----\\r\\nSee also: [trac:TracInstallPlatforms TracInstallPlatforms], TracGuide, TracUpgrade, TracPermissions\\r\\n', NULL, NULL);\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('TracBrowser', 1, 1362994208351603, 'trac', '127.0.0.1', '= The Trac Repository Browser =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nThe Trac repository browser can be used to browse specific revisions of directories \\r\\nand files stored in the repositories associated with the Trac environment.\\r\\n\\r\\n''''(since 0.12)'''': \\r\\nAt the top-level of the repository browser is the ''''''Repository Index'''''', \\r\\nlisting all the configured repositories. \\r\\nEach repository has a name which is used as a path prefix in a \\r\\n\"virtual\" file hierarchy encompassing all the available repositories.\\r\\nOne of the repositories can be configured with an empty name; this is the default repository.  When such a default repository is present, its top-level files and directories \\r\\nare also listed, in a ''''''Default Repository'''''' section placed before the \\r\\nrepository index. If the default repository is the only repository associated \\r\\nwith the Trac environment the ''''''Repository Index'''''' will be omitted ^[#note-multirepos (1)]^.\\r\\n\\r\\nDirectory entries are displayed in a list with sortable columns. The list \\r\\nentries can be sorted by ''''Name'''', ''''Size'''', ''''Age'''' or ''''Author'''' by clicking on the column\\r\\nheaders. The sort order can be reversed by clicking on a given column\\r\\nheader again.\\r\\n\\r\\nThe browser can be used to navigate through the directory structure \\r\\nby clicking on the directory names. \\r\\nClicking on a file name will show the contents of the file. \\r\\nClicking on the revision number of a file or directory will take \\r\\nyou to the TracRevisionLog for that file.\\r\\nNote that there''s also a ''''Revision Log'''' navigation link that will do the \\r\\nsame for the path currently being examined.\\r\\nClicking on the ''''diff'''' icon after revision number will display the changes made \\r\\nto the files modified in that revision.\\r\\nClicking on the ''''Age'''' of the file - will take you to that changeset in the timeline.\\r\\n\\r\\nIt''s also possible to browse directories or files as they were in history,\\r\\nat any given repository revision. The default behavior is to display the\\r\\nlatest revision but another revision number can easily be selected using\\r\\nthe ''''View revision'''' input field at the top of the page.\\r\\n\\r\\nThe color bar next to the ''''Age'''' column gives a visual indication of the age\\r\\nof the last change to a file or directory, following the convention that\\r\\n''''''[[span(style=color:#88f,blue)]]'''''' is oldest and ''''''[[span(style=color:#f88,red)]]''''''\\r\\nis newest, but this can be [TracIni#browser-section configured].\\r\\n\\r\\nAt the top of the browser page, there''s a ''''Visit'''' drop-down menu which you can use \\r\\nto select some interesting places in the repository, for example branches or tags. \\r\\nThis is sometimes referred to as the ''''browser quickjump'''' facility.\\r\\nThe precise meaning and content of this menu depends on your repository backend.\\r\\nFor Subversion, this list contains by default the top-level trunk directory \\r\\nand sub-directories of the top-level branches and tags directories \\r\\n(`/trunk`, `/branches/*`, and `/tags/*`).  This can be [TracIni#svn-section configured] \\r\\nfor more advanced cases.\\r\\n\\r\\nIf you''re using a Javascript enabled browser, you''ll be able to expand and \\r\\ncollapse directories in-place by clicking on the arrow head at the right side of a \\r\\ndirectory. Alternatively, the [trac:TracKeys keyboard] can also be used for this: \\r\\n - use `''j''` and `''k''` to select the next or previous entry, starting with the first\\r\\n - `''o''` (open) to toggle between expanded and collapsed state of the selected \\r\\n   directory or for visiting the selected file \\r\\n - `''v''` (view, visit) and `''<Enter>''`, same as above\\r\\n - `''r''` can be used to force the reload of an already expanded directory\\r\\n - `''A''` can be used to directly visit a file in annotate (blame) mode\\r\\n - `''L''` to view the log for the selected entry\\r\\nIf no row has been selected using `''j''` or `''k''` these keys will operate on the entry under the mouse.\\r\\n\\r\\n{{{#!comment\\r\\nMMM: I guess that some keys are upper case and some lower to avoid conflicts with browser defined keys.\\r\\nI find for example in Firefox and IE on windows that ''a'' works as well as ''A'' but ''l'' does not work for ''L''.\\r\\n cboos: ''l'' is reserved for Vim like behavior, see #7867\\r\\n}}}\\r\\n\\r\\nFor the Subversion backend, some advanced additional features are available:\\r\\n - The `svn:needs-lock` property will be displayed\\r\\n - Support for the `svn:mergeinfo` property showing the merged and eligible information\\r\\n - Support for browsing the `svn:externals` property \\r\\n   (which can be [TracIni#svn:externals-section configured])\\r\\n - The `svn:mime-type` property is used to select the syntax highlighter for rendering \\r\\n   the file. For example, setting `svn:mime-type` to `text/html` will ensure the file is \\r\\n   highlighted as HTML, regardless of the file extension. It also allows selecting the character \\r\\n   encoding used in the file content. For example, if the file content is encoded in UTF-8, \\r\\n   set `svn:mime-type` to `text/html;charset=utf-8`. The `charset=` specification overrides the \\r\\n   default encoding defined in the `default_charset` option of the `[trac]` section \\r\\n   of [TracIni#trac-section trac.ini].\\r\\n{{{#!comment\\r\\nMMM: I found this section a bit hard to understand. I changed the first item as I understood that well.\\r\\nbut I think the other items could be changed also\\r\\n cboos: in the meantime, I''ve added the ''''advanced'''' word as a hint this can be a bit complex...\\r\\n}}}\\r\\n\\r\\n\\r\\n----\\r\\n{{{#!div style=\"font-size:85%\"\\r\\n[=#note-multirepos (1)] This means that after upgrading a single-repository Trac of version \\r\\n0.11 (or below) to a multi-repository Trac (0.12), the repository browser will look and feel \\r\\nthe same, that single repository becoming automatically the \"default\" repository.\\r\\n}}}\\r\\n\\r\\nSee also: TracGuide, TracChangeset, TracFineGrainedPermissions\\r\\n', NULL, NULL),\n('TracNotification', 1, 1362994208355130, 'trac', '127.0.0.1', '= Email Notification of Ticket Changes =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nTrac supports notification of ticket changes via email. \\r\\n\\r\\nEmail notification is useful to keep users up-to-date on tickets/issues of interest, and also provides a convenient way to post all ticket changes to a dedicated mailing list. For example, this is how the [http://lists.edgewall.com/archive/trac-tickets/ Trac-tickets] mailing list is set up.\\r\\n\\r\\nDisabled by default, notification can be activated and configured in [wiki:TracIni trac.ini].\\r\\n\\r\\n== Receiving Notification Mails ==\\r\\nWhen reporting a new ticket or adding a comment, enter a valid email address or your username in the ''''reporter'''', ''''assigned to/owner'''' or ''''cc'''' field. Trac will automatically send you an email when changes are made to the ticket (depending on how notification is configured).\\r\\n\\r\\nThis is useful to keep up-to-date on an issue or enhancement request that interests you.\\r\\n\\r\\n=== How to use your username to receive notification mails ===\\r\\n\\r\\nTo receive notification mails, you can either enter a full email address or your username. To get notified with a simple username or login, you need to specify a valid email address in the ''''Preferences'''' page. \\r\\n\\r\\nAlternatively, a default domain name (''''''`smtp_default_domain`'''''') can be set in the TracIni file (see [#ConfigurationOptions Configuration Options] below). In this case, the default domain will be appended to the username, which can be useful for an \"Intranet\" kind of installation.\\r\\n\\r\\nWhen using apache and mod_kerb for authentication against Kerberos / Active Directory, usernames take the form (''''''`username@EXAMPLE.LOCAL`''''''). To avoid this being interpreted as an email address, add the Kerberos domain to  (''''''`ignore_domains`'''''').\\r\\n\\r\\n== Configuring SMTP Notification ==\\r\\n\\r\\n''''''Important:'''''' For TracNotification to work correctly, the `[trac] base_url` option must be set in [wiki:TracIni trac.ini]. \\r\\n\\r\\n=== Configuration Options ===\\r\\nThese are the available options for the `[notification]` section in trac.ini.\\r\\n\\r\\n * ''''''`smtp_enabled`'''''': Enable email notification.\\r\\n * ''''''`smtp_from`'''''': Email address to use for ''''Sender''''-headers in notification emails.\\r\\n * ''''''`smtp_from_name`'''''': Sender name to use for ''''Sender''''-headers in notification emails.\\r\\n * ''''''`smtp_from_author`'''''': (''''since 1.0'''') Use the author of a change (the reporter of a new ticket, or the author of a comment) as the `From:` header value in notification e-mails (default: false). If the author hasn''t set an e-mail address, `smtp_from` and `smtp_from_name` are used instead.\\r\\n * ''''''`smtp_replyto`'''''': Email address to use for ''''Reply-To''''-headers in notification emails.\\r\\n * ''''''`smtp_default_domain`'''''': (''''since 0.10'''') Append the specified domain to addresses that do not contain one. Fully qualified addresses are not modified. The default domain is appended to all username/login for which an email address cannot be found from the user settings.\\r\\n * ''''''`smtp_always_cc`'''''': List of email addresses to always send notifications to. ''''Typically used to post ticket changes to a dedicated mailing list.''''\\r\\n * ''''''`smtp_always_bcc`'''''': (''''since 0.10'''') List of email addresses to always send notifications to, but keeps addresses not visible from other recipients of the notification email \\r\\n * ''''''`smtp_subject_prefix`'''''': (''''since 0.10.1'''') Text that is inserted before the subject of the email. Set to \"!__default!__\" by default.\\r\\n * ''''''`always_notify_reporter`'''''':  Always send notifications to any address in the reporter field (default: false).\\r\\n * ''''''`always_notify_owner`'''''': (''''since 0.9'''') Always send notifications to the address in the owner field (default: false).\\r\\n * ''''''`always_notify_updater`'''''': (''''since 0.10'''') Always send a notification to the updater of a ticket (default: true).\\r\\n * ''''''`use_public_cc`'''''': (''''since 0.10'''') Addresses in To: (owner, reporter) and Cc: lists are visible by all recipients (default is ''''Bcc:'''' - hidden copy).\\r\\n * ''''''`use_short_addr`'''''': (''''since 0.10'''') Enable delivery of notifications to addresses that do not contain a domain (i.e. do not end with ''''@<domain.com>'''').This option is useful for intranets, where the SMTP server can handle local addresses and map the username/login to a local mailbox. See also `smtp_default_domain`. Do not use this option with a public SMTP server. \\r\\n * ''''''`ignore_domains`'''''': Comma-separated list of domains that should not be considered part of email addresses (for usernames with Kerberos domains).\\r\\n * ''''''`mime_encoding`'''''': (''''since 0.10'''') This option allows selecting the MIME encoding scheme. Supported values:\\r\\n   * `none`: default value, uses 7bit encoding if the text is plain ASCII, or 8bit otherwise. \\r\\n   * `base64`: works with any kind of content. May cause some issues with touchy anti-spam/anti-virus engines.\\r\\n   * `qp` or `quoted-printable`: best for european languages (more compact than base64) if 8bit encoding cannot be used.\\r\\n * ''''''`ticket_subject_template`'''''': (''''since 0.11'''') A [http://genshi.edgewall.org/wiki/Documentation/text-templates.html Genshi text template] snippet used to get the notification subject.\\r\\n * ''''''`email_sender`'''''': (''''since 0.12'''') Name of the component implementing `IEmailSender`. This component is used by the notification system to send emails. Trac currently provides the following components:\\r\\n   * `SmtpEmailSender`: connects to an SMTP server (default).\\r\\n   * `SendmailEmailSender`: runs a `sendmail`-compatible executable.\\r\\n\\r\\nEither ''''''`smtp_from`'''''' or ''''''`smtp_replyto`'''''' (or both) ''''must'''' be set, otherwise Trac refuses to send notification mails.\\r\\n\\r\\nThe following options are specific to email delivery through SMTP.\\r\\n * ''''''`smtp_server`'''''': SMTP server used for notification messages.\\r\\n * ''''''`smtp_port`'''''': (''''since 0.9'''') Port used to contact the SMTP server.\\r\\n * ''''''`smtp_user`'''''': (''''since 0.9'''') User name for authentication SMTP account.\\r\\n * ''''''`smtp_password`'''''': (''''since 0.9'''') Password for authentication SMTP account.\\r\\n * ''''''`use_tls`'''''': (''''since 0.10'''') Toggle to send notifications via a SMTP server using [http://en.wikipedia.org/wiki/Transport_Layer_Security TLS], such as GMail.\\r\\n\\r\\nThe following option is specific to email delivery through a `sendmail`-compatible executable.\\r\\n * ''''''`sendmail_path`'''''': (''''since 0.12'''') Path to the sendmail executable. The sendmail program must accept the `-i` and `-f` options.\\r\\n\\r\\n=== Example Configuration (SMTP) ===\\r\\n{{{\\r\\n[notification]\\r\\nsmtp_enabled = true\\r\\nsmtp_server = mail.example.com\\r\\nsmtp_from = notifier@example.com\\r\\nsmtp_replyto = myproj@projects.example.com\\r\\nsmtp_always_cc = ticketmaster@example.com, theboss+myproj@example.com\\r\\n}}}\\r\\n\\r\\n=== Example Configuration (`sendmail`) ===\\r\\n{{{\\r\\n[notification]\\r\\nsmtp_enabled = true\\r\\nemail_sender = SendmailEmailSender\\r\\nsendmail_path = /usr/sbin/sendmail\\r\\nsmtp_from = notifier@example.com\\r\\nsmtp_replyto = myproj@projects.example.com\\r\\nsmtp_always_cc = ticketmaster@example.com, theboss+myproj@example.com\\r\\n}}}\\r\\n\\r\\n=== Customizing the e-mail subject ===\\r\\nThe e-mail subject can be customized with the `ticket_subject_template` option, which contains a [http://genshi.edgewall.org/wiki/Documentation/text-templates.html Genshi text template] snippet. The default value is:\\r\\n{{{\\r\\n$prefix #$ticket.id: $summary\\r\\n}}}\\r\\nThe following variables are available in the template:\\r\\n\\r\\n * `env`: The project environment (see [trac:source:/trunk/trac/env.py env.py]).\\r\\n * `prefix`: The prefix defined in `smtp_subject_prefix`.\\r\\n * `summary`: The ticket summary, with the old value if the summary was edited.\\r\\n * `ticket`: The ticket model object (see [trac:source:/trunk/trac/ticket/model.py model.py]). Individual ticket fields can be addressed by appending the field name separated by a dot, e.g. `$ticket.milestone`.\\r\\n\\r\\n=== Customizing the e-mail content ===\\r\\n\\r\\nThe notification e-mail content is generated based on `ticket_notify_email.txt` in `trac/templates`.  You can add your own version of this template by adding a `ticket_notify_email.txt` to the templates directory of your environment. The default looks like this:\\r\\n\\r\\n{{{\\r\\n$ticket_body_hdr\\r\\n$ticket_props\\r\\n{% choose ticket.new %}\\\\\\r\\n{%   when True %}\\\\\\r\\n$ticket.description\\r\\n{%   end %}\\\\\\r\\n{%   otherwise %}\\\\\\r\\n{%     if changes_body %}\\\\\\r\\n${_(''Changes (by %(author)s):'', author=change.author)}\\r\\n\\r\\n$changes_body\\r\\n{%     end %}\\\\\\r\\n{%     if changes_descr %}\\\\\\r\\n{%       if not changes_body and not change.comment and change.author %}\\\\\\r\\n${_(''Description changed by %(author)s:'', author=change.author)}\\r\\n{%       end %}\\\\\\r\\n$changes_descr\\r\\n--\\r\\n{%     end %}\\\\\\r\\n{%     if change.comment %}\\\\\\r\\n\\r\\n${changes_body and _(''Comment:'') or _(''Comment (by %(author)s):'', author=change.author)}\\r\\n\\r\\n$change.comment\\r\\n{%     end %}\\\\\\r\\n{%   end %}\\\\\\r\\n{% end %}\\\\\\r\\n\\r\\n-- \\r\\n${_(''Ticket URL: <%(link)s>'', link=ticket.link)}\\r\\n$project.name <${project.url or abs_href()}>\\r\\n$project.descr\\r\\n}}}\\r\\n== Sample Email ==\\r\\n{{{\\r\\n#42: testing\\r\\n---------------------------+------------------------------------------------\\r\\n       Id:  42             |      Status:  assigned                \\r\\nComponent:  report system  |    Modified:  Fri Apr  9 00:04:31 2004\\r\\n Severity:  major          |   Milestone:  0.9                     \\r\\n Priority:  lowest         |     Version:  0.6                     \\r\\n    Owner:  anonymous      |    Reporter:  jonas@example.com               \\r\\n---------------------------+------------------------------------------------\\r\\nChanges:\\r\\n  * component:  changset view => search system\\r\\n  * priority:  low => highest\\r\\n  * owner:  jonas => anonymous\\r\\n  * cc:  daniel@example.com =>\\r\\n         daniel@example.com, jonas@example.com\\r\\n  * status:  new => assigned\\r\\n\\r\\nComment:\\r\\nI''m interested too!\\r\\n\\r\\n--\\r\\nTicket URL: <http://example.com/trac/ticket/42>\\r\\nMy Project <http://myproj.example.com/>\\r\\n}}}\\r\\n\\r\\n\\r\\n== Customizing e-mail content for MS Outlook ==\\r\\n\\r\\nOut-of-the-box, MS Outlook normally presents plain text e-mails with a variable-width font; the ticket properties table will most certainly look like a mess in MS Outlook. This can be fixed with some customization of the [#Customizingthee-mailcontent e-mail template].\\r\\n\\r\\nReplace the following second row in the template:\\r\\n{{{\\r\\n$ticket_props\\r\\n}}}\\r\\n\\r\\nwith this instead (''''requires Python 2.6 or later''''):\\r\\n{{{\\r\\n--------------------------------------------------------------------------\\r\\n{% with\\r\\n   pv = [(a[0].strip(), a[1].strip()) for a in [b.split('':'') for b in\\r\\n         [c.strip() for c in \\r\\n          ticket_props.replace(''|'', ''\\\\n'').splitlines()[1:-1]] if '':'' in b]];\\r\\n   sel = [''Reporter'', ''Owner'', ''Type'', ''Status'', ''Priority'', ''Milestone'', \\r\\n          ''Component'', ''Severity'', ''Resolution'', ''Keywords''] %}\\\\\\r\\n${''\\\\n''.join(''%s\\\\t%s'' % (format(p[0]+'':'', '' <12''), p[1]) for p in pv if p[0] in sel)}\\r\\n{% end %}\\\\\\r\\n--------------------------------------------------------------------------\\r\\n}}}\\r\\n\\r\\nThe table of ticket properties is replaced with a list of a selection of the properties. A tab character separates the name and value in such a way that most people should find this more pleasing than the default table, when using MS Outlook.\\r\\n{{{#!div style=\"margin: 1em 1.75em; border:1px dotted\"\\r\\n{{{#!html\\r\\n#42: testing<br />\\r\\n--------------------------------------------------------------------------<br />\\r\\n<table cellpadding=0>\\r\\n<tr><td>Reporter:</td><td>jonas@example.com</td></tr>\\r\\n<tr><td>Owner:</td><td>anonymous</td></tr>\\r\\n<tr><td>Type:</td><td>defect</td></tr>\\r\\n<tr><td>Status:</td><td>assigned</td></tr>\\r\\n<tr><td>Priority:</td><td>lowest</td></tr>\\r\\n<tr><td>Milestone:</td><td>0.9</td></tr>\\r\\n<tr><td>Component:</td><td>report system</td></tr>\\r\\n<tr><td>Severity:</td><td>major</td></tr>\\r\\n<tr><td>Resolution:</td><td> </td></tr>\\r\\n<tr><td>Keywords:</td><td> </td></tr>\\r\\n</table>\\r\\n--------------------------------------------------------------------------<br />\\r\\nChanges:<br />\\r\\n<br />\\r\\n&nbsp;&nbsp;* component: &nbsp;changset view =&gt; search system<br />\\r\\n&nbsp;&nbsp;* priority: &nbsp;low =&gt; highest<br />\\r\\n&nbsp;&nbsp;* owner: &nbsp;jonas =&gt; anonymous<br />\\r\\n&nbsp;&nbsp;* cc: &nbsp;daniel@example.com =&gt;<br />\\r\\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;daniel@example.com, jonas@example.com<br />\\r\\n&nbsp;&nbsp;* status: &nbsp;new =&gt; assigned<br />\\r\\n<br />\\r\\nComment:<br />\\r\\nI''m interested too!<br />\\r\\n<br />\\r\\n--<br />\\r\\nTicket URL: &lt;http://example.com/trac/ticket/42&gt;<br />\\r\\nMy Project &lt;http://myproj.example.com/&gt;<br />\\r\\n}}}\\r\\n}}}\\r\\n\\r\\n**Important**: Only those ticket fields that are listed in `sel` are part of the HTML mail. If you have defined custom ticket fields which shall be part of the mail they have to be added to `sel`, example:\\r\\n{{{\\r\\n   sel = [''Reporter'', ..., ''Keywords'', ''Custom1'', ''Custom2'']\\r\\n}}}\\r\\n\\r\\nHowever, it''s not as perfect as an automatically HTML-formatted e-mail would be, but presented ticket properties are at least readable by default in MS Outlook...\\r\\n\\r\\n\\r\\n== Using GMail as the SMTP relay host ==\\r\\n\\r\\nUse the following configuration snippet\\r\\n{{{\\r\\n[notification]\\r\\nsmtp_enabled = true\\r\\nuse_tls = true\\r\\nmime_encoding = base64\\r\\nsmtp_server = smtp.gmail.com\\r\\nsmtp_port = 587\\r\\nsmtp_user = user\\r\\nsmtp_password = password\\r\\n}}}\\r\\n\\r\\nwhere ''''user'''' and ''''password'''' match an existing GMail account, ''''i.e.'''' the ones you use to log in on [http://gmail.com]\\r\\n\\r\\nAlternatively, you can use `smtp_port = 25`.[[br]]\\r\\nYou should not use `smtp_port = 465`. It will not work and your ticket submission may deadlock. Port 465 is reserved for the SMTPS protocol, which is not supported by Trac. See [comment:ticket:7107:2 #7107] for details.\\r\\n \\r\\n== Filtering notifications for one''s own changes ==\\r\\nIn Gmail, use the filter:\\r\\n\\r\\n{{{\\r\\nfrom:(<smtp_from>) ((\"Reporter: <username>\" -Changes) OR \"Changes (by <username>)\")\\r\\n}}}\\r\\n\\r\\nFor Trac .10, use the filter:\\r\\n{{{\\r\\nfrom:(<smtp_from>) ((\"Reporter: <username>\" -Changes -Comment) OR \"Changes (by <username>)\" OR \"Comment (by <username>)\")\\r\\n}}}\\r\\n\\r\\nto delete these notifications.\\r\\n\\r\\nIn Thunderbird, there is no such solution if you use IMAP\\r\\n(see http://kb.mozillazine.org/Filters_(Thunderbird)#Filtering_the_message_body).\\r\\n\\r\\nThe best you can do is to set \"always_notify_updater\" in conf/trac.ini to false.\\r\\nYou will however still get an email if you comment a ticket that you own or have reported.\\r\\n\\r\\nYou can also add this plugin:\\r\\nhttp://trac-hacks.org/wiki/NeverNotifyUpdaterPlugin\\r\\n\\r\\n== Troubleshooting ==\\r\\n\\r\\nIf you cannot get the notification working, first make sure the log is activated and have a look at the log to find if an error message has been logged. See TracLogging for help about the log feature.\\r\\n\\r\\nNotification errors are not reported through the web interface, so the user who submit a change or a new ticket never gets notified about a notification failure. The Trac administrator needs to look at the log to find the error trace.\\r\\n\\r\\n=== ''''Permission denied'''' error ===\\r\\n\\r\\nTypical error message:\\r\\n{{{\\r\\n  ...\\r\\n  File \".../smtplib.py\", line 303, in connect\\r\\n    raise socket.error, msg\\r\\n  error: (13, ''Permission denied'')\\r\\n}}}\\r\\n\\r\\nThis error usually comes from a security settings on the server: many Linux distributions do not let the web server (Apache, ...) to post email message to the local SMTP server.\\r\\n\\r\\nMany users get confused when their manual attempts to contact the SMTP server succeed:\\r\\n{{{\\r\\ntelnet localhost 25\\r\\n}}}\\r\\nThe trouble is that a regular user may connect to the SMTP server, but the web server cannot:\\r\\n{{{\\r\\nsudo -u www-data telnet localhost 25\\r\\n}}}\\r\\n\\r\\nIn such a case, you need to configure your server so that the web server is authorized to post to the SMTP server. The actual settings depend on your Linux distribution and current security policy. You may find help browsing the Trac [trac:MailingList MailingList] archive.\\r\\n\\r\\nRelevant ML threads:\\r\\n * SELinux: http://article.gmane.org/gmane.comp.version-control.subversion.trac.general/7518\\r\\n\\r\\nFor SELinux in Fedora 10:\\r\\n{{{\\r\\n$ setsebool -P httpd_can_sendmail 1\\r\\n}}}\\r\\n=== ''''Suspected spam'''' error ===\\r\\n\\r\\nSome SMTP servers may reject the notification email sent by Trac.\\r\\n\\r\\nThe default Trac configuration uses Base64 encoding to send emails to the recipients. The whole body of the email is encoded, which sometimes trigger ''''false positive'''' SPAM detection on sensitive email servers. In such an event, it is recommended to change the default encoding to \"quoted-printable\" using the `mime_encoding` option.\\r\\n\\r\\nQuoted printable encoding works better with languages that use one of the Latin charsets. For Asian charsets, it is recommended to stick with the Base64 encoding.\\r\\n\\r\\n=== ''''501, 5.5.4 Invalid Address'''' error ===\\r\\n\\r\\nOn IIS 6.0 you could get a \\r\\n{{{\\r\\nFailure sending notification on change to ticket #1: SMTPHeloError: (501, ''5.5.4 Invalid Address'')\\r\\n}}}\\r\\nin the trac log. Have a look [http://support.microsoft.com/kb/291828 here] for instructions on resolving it.\\r\\n\\r\\n\\r\\n----\\r\\nSee also: TracTickets, TracIni, TracGuide\\r\\n', NULL, NULL),\n('TracBatchModify', 1, 1362994208359649, 'trac', '127.0.0.1', '= Trac Ticket Batch Modification =\\n[[TracGuideToc]]\\n\\nFrom [wiki:TracQuery custom query] results Trac provides support for modifying a batch of tickets in one request.\\n\\nTo perform a batch modification select the tickets you wish to modify and set the new field values using the section underneath the query results. \\n\\n== List fields\\n\\nThe `Keywords` and `Cc` fields are treated as lists, where list items can be added and/or removed in addition of replacing the entire list value. All list field controls accept multiple items (i.e. multiple keywords or cc addresses).\\n', NULL, NULL),\n('TracRepositoryAdmin', 1, 1362994208361398, 'trac', '127.0.0.1', '= Repository Administration =\\r\\n[[PageOutline(2-3)]]\\r\\n\\r\\n== Quick start == #QuickStart\\r\\n\\r\\n * Manage repositories in the \"Repository\" admin panel, with `trac-admin` or in the `[repositories]` section of [wiki:TracIni#repositories-section trac.ini].\\r\\n * Set up a call to `trac-admin $ENV changeset added $REPO $REV` in the post-commit hook of each repository. Additionally, add a call to `trac-admin $ENV changeset modified $REPO $REV` in the post-revprop-change hook of repositories allowing revision property changes.\\r\\n * Set the `[trac] repository_sync_per_request` option to an empty value to disable per-request syncing.\\r\\n * Make sure the user under which your Subversion hooks are run has write access to the Trac environment, or use a tool like `sudo` to temporarily elevate privileges.\\r\\n\\r\\n== Specifying repositories == #Repositories\\r\\nStarting with 0.12, Trac can handle more than one repository per environment. The pre-0.12 way of specifying the repository with the `repository_dir` and `repository_type` options in the `[trac]` section of [wiki:TracIni trac.ini] is still supported, but two new mechanisms allow including additional repositories into an environment.\\r\\n\\r\\nIt is also possible to define aliases of repositories, that act as \"pointers\" to real repositories. This can be useful when renaming a repository, to avoid breaking all the links to the old name.\\r\\n\\r\\nA number of attributes can be associated with each repository, which define the repository''s location, type, name and how it is displayed in the source browser. The following attributes are supported:\\r\\n\\r\\n||=''''''Attribute'''''' =||=''''''Description'''''' =||\\r\\n||`alias` ||\\\\\\r\\n||A repository having an `alias` attribute is an alias to a real repository. All TracLinks referencing the alias resolve to the aliased repository. Note that multiple indirection is not supported, so an alias must always point to a real repository. The `alias` and `dir` attributes are mutually exclusive. ||\\r\\n||`description` ||\\\\\\r\\n||The text specified in the `description` attribute is displayed below the top-level entry for the repository in the source browser. It supports WikiFormatting. ||\\r\\n||`dir` ||\\\\\\r\\n||The `dir` attribute specifies the location of the repository in the filesystem. It corresponds to the value previously specified in the option `[trac] repository_dir`. The `alias` and `dir` attributes are mutually exclusive. ||\\r\\n||`hidden` ||When set to `true`, the repository is hidden from the repository index page in the source browser. Browsing the repository is still possible, and links referencing the repository remain valid. ||\\r\\n||`type` ||The `type` attribute sets the type of version control system used by the repository. Trac supports Subversion and Git out-of-the-box, and plugins add support for many other systems. If `type` is not specified, it defaults to the value of the `[trac] repository_type` option. ||\\r\\n||`url` ||The `url` attribute specifies the root URL to be used for checking out from the repository. When specified, a \"Repository URL\" link is added to the context navigation links in the source browser, that can be copied into the tool used for creating the working copy. ||\\r\\n\\r\\nA repository `name` and one of `alias` or `dir` attributes are mandatory. All others are optional.\\r\\n\\r\\nAfter adding a repository, the cache for that repository must be re-synchronized once with the `trac-admin $ENV repository resync` command.\\r\\n\\r\\n `repository resync <repos>`::\\r\\n   Re-synchronize Trac with a repository.\\r\\n\\r\\n\\r\\n=== In `trac.ini` === #ReposTracIni\\r\\nRepositories and repository attributes can be specified in the `[repositories]` section of [wiki:TracIni#repositories-section trac.ini]. Every attribute consists of a key structured as `{name}.{attribute}` and the corresponding value separated with an equal sign (`=`). The name of the default repository is empty.\\r\\n\\r\\nThe main advantage of specifying repositories in `trac.ini` is that they can be inherited from a global configuration (see the [wiki:TracIni#GlobalConfiguration global configuration] section of TracIni). One drawback is that, due to limitations in the `ConfigParser` class used to parse `trac.ini`, the repository name is always all-lowercase.\\r\\n\\r\\nThe following example defines two Subversion repositories named `project` and `lib`, and an alias to `project` as the default repository. This is a typical use case where a Trac environment previously had a single repository (the `project` repository), and was converted to multiple repositories. The alias ensures that links predating the change continue to resolve to the `project` repository.\\r\\n{{{\\r\\n#!ini\\r\\n[repositories]\\r\\nproject.dir = /var/repos/project\\r\\nproject.description = This is the ''''main'''' project repository.\\r\\nproject.type = svn\\r\\nproject.url = http://example.com/svn/project\\r\\nproject.hidden = true\\r\\n\\r\\nlib.dir = /var/repos/lib\\r\\nlib.description = This is the secondary library code.\\r\\nlib.type = svn\\r\\nlib.url = http://example.com/svn/lib\\r\\n\\r\\n.alias = project\\r\\n}}}\\r\\nNote that `name.alias = target` makes `name` an alias for the `target` repo, not the other way around.\\r\\n\\r\\n=== In the database === #ReposDatabase\\r\\nRepositories can also be specified in the database, using either the \"Repositories\" admin panel under \"Version Control\", or the `trac-admin $ENV repository` commands.\\r\\n\\r\\nThe admin panel shows the list of all repositories defined in the Trac environment. It allows adding repositories and aliases, editing repository attributes and removing repositories. Note that repositories defined in `trac.ini` are displayed but cannot be edited.\\r\\n\\r\\nThe following [wiki:TracAdmin trac-admin] commands can be used to perform repository operations from the command line.\\r\\n\\r\\n `repository add <repos> <dir> [type]`::\\r\\n   Add a repository `<repos>` located at `<dir>`, and optionally specify its type.\\r\\n\\r\\n `repository alias <name> <target>`::\\r\\n   Create an alias `<name>` for the repository `<target>`.\\r\\n\\r\\n `repository remove <repos>`::\\r\\n   Remove the repository `<repos>`.\\r\\n\\r\\n `repository set <repos> <key> <value>`::\\r\\n   Set the attribute `<key>` to `<value>` for the repository `<repos>`. \\r\\n\\r\\nNote that the default repository has an empty name, so it will likely need to be quoted when running `trac-admin` from a shell. Alternatively, the name \"`(default)`\" can be used instead, for example when running `trac-admin` in interactive mode.\\r\\n\\r\\n\\r\\n== Repository synchronization == #Synchronization\\r\\nPrior to 0.12, Trac synchronized its cache with the repository on every HTTP request. This approach is not very efficient and not practical anymore with multiple repositories. For this reason, explicit synchronization through post-commit hooks was added. \\r\\n\\r\\nThere is also new functionality in the form of a repository listener extension point ''''(IRepositoryChangeListener)'''' that is triggered by the post-commit hook when a changeset is added or modified, and can be used by plugins to perform actions on commit.\\r\\n\\r\\n=== Mercurial Repositories ===\\r\\nPlease note that at the time of writing, no initial resynchronization or any hooks are necessary for Mercurial repositories - see [trac:#9485] for more information. \\r\\n\\r\\n=== Explicit synchronization === #ExplicitSync\\r\\nThis is the preferred method of repository synchronization. It requires setting the `[trac]  repository_sync_per_request` option in [wiki:TracIni#trac-section trac.ini] to an empty value, and adding a call to `trac-admin` in the post-commit hook of each repository. Additionally, if a repository allows changing revision metadata, a call to `trac-admin` must be added to the post-revprop-change hook as well.\\r\\n\\r\\n `changeset added <repos> <rev> [...]`::\\r\\n   Notify Trac that one or more changesets have been added to a repository.\\r\\n\\r\\n `changeset modified <repos> <rev> [...]`::\\r\\n   Notify Trac that metadata on one or more changesets in a repository has been modified.\\r\\n\\r\\nThe `<repos>` argument can be either a repository name (use \"`(default)`\" for the default repository) or the path to the repository.\\r\\n\\r\\nNote that you may have to set the environment variable PYTHON_EGG_CACHE to the same value as was used for the web server configuration before calling trac-admin, if you changed it from its default location. See [wiki:TracPlugins Trac Plugins] for more information.\\r\\n\\r\\nThe following examples are complete post-commit and post-revprop-change scripts for Subversion. They should be edited for the specific environment, marked executable (where applicable) and placed in the `hooks` directory of each repository. On Unix (`post-commit`):\\r\\n{{{#!sh\\r\\n#!/bin/sh\\r\\nexport PYTHON_EGG_CACHE=\"/path/to/dir\"\\r\\n/usr/bin/trac-admin /path/to/env changeset added \"$1\" \"$2\"\\r\\n}}}\\r\\nNote: Ubuntu doesn''t seem to like /usr/bin/trac-admin, so just use:\\r\\n{{{#!sh\\r\\n#!/bin/sh\\r\\nexport PYTHON_EGG_CACHE=\"/path/to/dir\"\\r\\ntrac-admin /path/to/env/ changeset added \"$1\" \"$2\"\\r\\n}}}\\r\\nOn Windows (`post-commit.cmd`):\\r\\n{{{#!application/x-dos-batch\\r\\n@C:\\\\Python26\\\\Scripts\\\\trac-admin.exe C:\\\\path\\\\to\\\\env changeset added \"%1\" \"%2\"\\r\\n}}}\\r\\n\\r\\nThe post-revprop-change hook for Subversion is very similar. On Unix (`post-revprop-change`):\\r\\n{{{#!sh\\r\\n#!/bin/sh\\r\\nexport PYTHON_EGG_CACHE=\"/path/to/dir\"\\r\\n/usr/bin/trac-admin /path/to/env changeset modified \"$1\" \"$2\"\\r\\n}}}\\r\\nOn Windows (`post-revprop-change.cmd`):\\r\\n{{{#!application/x-dos-batch\\r\\n@C:\\\\Python26\\\\Scripts\\\\trac-admin.exe C:\\\\path\\\\to\\\\env changeset modified \"%1\" \"%2\"\\r\\n}}}\\r\\n\\r\\nThe Unix variants above assume that the user running the Subversion commit has write access to the Trac environment, which is the case in the standard configuration where both the repository and Trac are served by the web server. If you access the repository through another means, for example `svn+ssh://`, you may have to run `trac-admin` with different privileges, for example by using `sudo`.\\r\\n\\r\\nNote that calling `trac-admin` in your Subversion hooks can slow down the commit and log editing operations on the client side. You might want to use the [trac:source:trunk/contrib/trac-svn-hook contrib/trac-svn-hook] script which starts `trac-admin` in an asynchronous way. The script also comes with a number of safety checks and usage advices which should make it easier to set up and test your hooks. There''s no equivalent `trac-svn-hook.bat` for Windows yet, but the script can be run by Cygwin''s bash.\\r\\n\\r\\nSee the [http://svnbook.red-bean.com/en/1.5/svn.reposadmin.create.html#svn.reposadmin.create.hooks section about hooks] in the Subversion book for more information. Other repository types will require different hook setups.\\r\\n\\r\\nGit hooks can be used in the same way for explicit syncing of git repositories. Add the following to `.git/hooks/post-commit`:\\r\\n{{{#!sh\\r\\nREV=$(git rev-parse HEAD)\\r\\ntrac-admin /path/to/env changeset added <my-repository> $REV\\r\\n}}}\\r\\n\\r\\nFor Mercurial, add the following entries to the `.hgrc` file of each repository accessed by Trac (if [trac:TracMercurial] is installed in a Trac `plugins` directory, download [trac:source:mercurial-plugin/tracext/hg/hooks.py hooks.py] and place it somewhere accessible):\\r\\n{{{#!ini\\r\\n[hooks]\\r\\n; If mercurial-plugin is installed globally\\r\\ncommit = python:tracext.hg.hooks.add_changesets\\r\\nchangegroup = python:tracext.hg.hooks.add_changesets\\r\\n\\r\\n; If mercurial-plugin is installed in a Trac plugins directory\\r\\ncommit = python:/path/to/hooks.py:add_changesets\\r\\nchangegroup = python:/path/to/hooks.py:add_changesets\\r\\n\\r\\n[trac]\\r\\nenv = /path/to/env\\r\\ntrac-admin = /path/to/trac-admin\\r\\n}}}\\r\\n\\r\\n=== Per-request synchronization === #PerRequestSync\\r\\nIf the post-commit hooks are not available, the environment can be set up for per-request synchronization. In that case, the `[trac] repository_sync_per_request` option in [wiki:TracIni#trac-section trac.ini] must be set to a comma-separated list of repository names to be synchronized.\\r\\n\\r\\nNote that in this case, the changeset listener extension point is not called, and therefore plugins using it will not work correctly.\\r\\n\\r\\n\\r\\n== Migration from a single-repository setup (Subversion) == #Migration\\r\\nThe following procedure illustrates a typical migration from a Subversion single-repository setup to multiple repositories.\\r\\n\\r\\n 1. Remove the default repository specification from the `[trac] repository_dir` option.\\r\\n 1. Add the main repository as a named repository.\\r\\n 1. Re-synchronize the main repository.\\r\\n 1. Set up post-commit and post-revprop-change hooks on the \"main\" repository, and set `[trac] repository_sync_per_request` to an empty value.\\r\\n 1. Add an alias to the main repository as the default repository (by leaving out the the `name`, e.g. `.alias = main`). This ensures that all links predating the migration still resolve to the main repository.\\r\\n 1. Repeat steps 2, 3 and 4 to add other \"named\" repositories as needed.\\r\\n\\r\\n== Migration from a single-repository setup (Mercurial) == #MigrationMercurial\\r\\nThe following procedure illustrates a typical migration from a Mercurial single-repository setup to multiple repositories. Please note that at the time of writing, no initial resynchronization or any hooks are necessary for Mercurial repositories - see [trac:ticket:9485 #9485] for more information.\\r\\n\\r\\n 1. Upgrade to the latest version of the TracMercurial plugin.\\r\\n 1. Remove the default repository specification from the `[trac] repository_dir` option.\\r\\n 1. Add the main repository as a named repository.\\r\\n 1. Add an alias to the main repository as the default repository (by leaving out the the `name`, e.g. `.alias = main`). This ensures that all links predating the migration still resolve to the main repository.\\r\\n 1. Repeat step 3 to add other \"named\" repositories as needed.\\r\\n\\r\\n== Troubleshooting ==\\r\\n\\r\\n=== My trac-post-commit-hook doesn''t work anymore === #trac-post-commit-hook\\r\\n\\r\\nYou must now use the optional components from `tracopt.ticket.commit_updater.*`, which you can activate through the Plugins panel in the Administrative part of the web interface, or by directly modifying the [TracIni#components-section \"[components]\"] section in the trac.ini. Be sure to use [#ExplicitSync explicit synchronization] as explained above.\\r\\n', NULL, NULL),\n('TracSupport', 1, 1362994208363747, 'trac', '127.0.0.1', '= Trac Support =\\r\\n\\r\\nLike in most [http://www.opensource.org/ open source projects], \"free\" Trac support is available primarily through the community itself, mainly through the [trac:MailingList mailing list] and the [trac: project wiki]. The latter is the authoritative source for the TracGuide (administrator and user guides for Trac).\\r\\n\\r\\nThere is also an [trac:IrcChannel IRC channel], where people might be able to help out. Much of the ''live'' development discussions also happen there.\\r\\n\\r\\nBefore you start a new support query, make sure you''ve done the appropriate searching:\\r\\n * in the project''s [trac:TracFaq FAQ]\\r\\n * in past messages to the [http://groups.google.com/group/trac-users Trac Users Mailing List]\\r\\n * in the Trac ticket system, using either a [trac:search:?q=&ticket=on&wiki=on full search] or a [trac:query: ticket query].\\r\\n\\r\\nPlease ''''''don''t'''''' create a ticket in trac.egdewall.org for asking a support question about Trac. Only use it when you face a ''''real'''' and ''''new'''' bug in Trac, and do so only after having read the [trac:NewTicketGuidelines NewTicketGuidelines]. The more a bug report or enhancement request complies with those guidelines, the higher the chances are that it will be fixed or implemented promptly!\\r\\n\\r\\n----\\r\\nSee also: [trac:MailingList], [trac:TracTroubleshooting], [trac:CommercialServices]\\r\\n', NULL, NULL),\n('TracNavigation', 1, 1362994208365240, 'trac', '127.0.0.1', '= Trac Navigation =\\r\\n\\r\\nStarting with Trac 0.11, it is now possible to customize the main and meta navigation entries in some basic ways.\\r\\n\\r\\nThe new `[mainnav]` and `[metanav]` configuration sections can now be used to customize the text and link used for the navigation items, or even to disable them.  The `mainnav` and `metanav` options in the `[trac]` configuration section can also be used to change the order.\\r\\n\\r\\n=== `[mainnav]` #mainnav-bar\\r\\n`[mainnav]` corresponds to the ''''''main navigation bar'''''', the one containing entries such as ''''Wiki'''', ''''Timeline'''', ''''Roadmap'''', ''''Browse Source'''' and so on. This navigation bar is meant to access the default page of the main modules enabled in Trac that are accessible for the current user.\\r\\n\\r\\n\\r\\n** [=#Example Example] ** \\r\\n\\r\\nIn the following example, we rename the link to the Wiki start \"Home\", and make the \"View Tickets\" entry link to a specific report.  The second example (below) also hides the \"!Help/Guide\" link.\\r\\n\\r\\nRelevant excerpt from the TracIni:\\r\\n{{{\\r\\n[mainnav]\\r\\nwiki.label = Home\\r\\ntickets.href = /report/24\\r\\n}}}\\r\\n\\r\\n=== `[metanav]` #metanav-bar\\r\\n`[metanav]` corresponds to the ''''''meta navigation bar'''''', by default positioned above the main navigation bar and below the ''''Search'''' box. It contains the ''''Log in'''', ''''Logout'''', ''''!Help/Guide'''' etc. entries. This navigation bar is meant to access some global information about the Trac project and the current user.\\r\\n\\r\\nThere is one special entry in the  `[metanav]` section: `logout.redirect` is the page the user sees after hitting the logout button. \\r\\n[[comment(see also #Trac3808)]]\\r\\n\\r\\n** Example ** \\r\\n\\r\\n{{{\\r\\n[metanav]\\r\\nhelp = disabled\\r\\nlogout.redirect = wiki/Logout\\r\\n}}}\\r\\n\\r\\n\\r\\n=== Notes\\r\\nPossible URL formats (for `.href` or `.redirect`):\\r\\n|| ''''''config'''''' || ''''''redirect to'''''' ||\\r\\n|| `wiki/Logout` || `/projects/env/wiki/Logout` ||\\r\\n|| `http://hostname/` || `http://hostname/` ||\\r\\n|| `/projects` || `/projects` ||\\r\\n\\r\\n\\r\\n=== `[trac]` #nav-order\\r\\nThe `mainnav` and `metanav` options in the `[trac]` configuration section control the order in which the navigation items are displayed (left to right).  This can be useful with plugins that add navigation items.\\r\\n\\r\\n** Example ** \\r\\n\\r\\nIn the following example, we change the order to prioritise the ticket related items further left.\\r\\n\\r\\nRelevant excerpt from the TracIni:\\r\\n{{{\\r\\n[trac]\\r\\nmainnav = wiki,tickets,newticket,timeline,roadmap,browser,search,admin\\r\\n}}}\\r\\n\\r\\nThe default order and item names can be viewed in the [TracIni#trac-section trac section of TracIni].\\r\\n\\r\\n=== Context Navigation #ctxtnav-bar\\r\\n\\r\\nNote that it is still not possible to customize the ''''''contextual navigation bar'''''', i.e. the one usually placed below the main navigation bar.\\r\\n\\r\\n\\r\\n----\\r\\nSee also: TracInterfaceCustomization, and the [http://trac-hacks.org/wiki/NavAddPlugin TracHacks:NavAddPlugin] or [http://trac-hacks.org/wiki/MenusPlugin TracHacks:MenusPlugin] (still needed for adding entries)', NULL, NULL),\n('SandBox', 1, 1362994208366886, 'trac', '127.0.0.1', '= The Sandbox =\\r\\n\\r\\nThis is just a page to practice and learn WikiFormatting. \\r\\n\\r\\nGo ahead, edit it freely.\\r\\n', NULL, NULL),\n('TracRevisionLog', 1, 1362994208368308, 'trac', '127.0.0.1', '= Viewing Revision Logs =\\r\\n[[TracGuideToc]]\\r\\n\\r\\nWhen you browse the repository, it''s always possible to query the \\r\\n''''Revision Log'''' view corresponding to the path you''re currently seeing.\\r\\nThis will display a list of the most recent changesets in which the \\r\\ncurrent path or any other path below it has been modified.\\r\\n\\r\\n== The Revision Log Form ==\\r\\n\\r\\nIt''s possible to set the revision at which the revision log should\\r\\nstart, using the ''''View log starting at'''' field. An empty value\\r\\nor a value of ''''head'''' is taken to be the newest changeset. \\r\\n\\r\\nIt''s also possible to specify the revision at which the log should\\r\\nstop, using the ''''back to'''' field. By default, it''s left empty, \\r\\nwhich means the revision log will stop as soon as 100 revisions have \\r\\nbeen listed.\\r\\n\\r\\nAlso, there are three modes of operation of the revision log.\\r\\n\\r\\nBy default, the revision log ''''stops on copy'''', which means that \\r\\nwhenever an ''''Add'''', ''''Copy'''' or ''''Rename'''' operation is detected, \\r\\nno older revision will be shown. That''s very convenient when working\\r\\nwith branches, as one only sees the history corresponding to what\\r\\nhas been done on the branch.\\r\\n\\r\\nIt''s also possible to indicate that one wants to see what happened\\r\\nbefore a ''''Copy'''' or ''''Rename'''' change, by selecting the \\r\\n''''Follow copies'''' mode. This will cross all copies or renames changes.\\r\\nEach time the name of the path changes, there will be an additional\\r\\nindentation level. That way, the changes on the different paths\\r\\nare easily grouped together visually.\\r\\n\\r\\nIt''s even possible to go past an ''''Add'''' change, in order to see \\r\\nif there has been a ''''Delete'''' change on that path, before \\r\\nthat ''''Add''''. This mode corresponds to the mode called \\r\\n''''Show only adds, moves and deletes''''. \\r\\nWhile quite useful at times, be aware that this operation is quite \\r\\nresource intensive.\\r\\n\\r\\nFinally, there''s also a checkbox ''''Show full log messages'''',\\r\\nwhich controls whether the full content of the commit log message\\r\\nshould be displayed for each change, or only a shortened version of it.\\r\\n\\r\\n== The Revision Log Information ==\\r\\n\\r\\nFor each revision log entry, there are 7 columns:\\r\\n 1. The first column contains a pair of radio buttons and should be used \\r\\n    for selecting the ''''old'''' and the ''''new'''' revisions that will be \\r\\n    used for [wiki:TracRevisionLog#viewingtheactualchanges viewing the actual changes].\\r\\n 1. A color code (similar to the one used for the\\r\\n    [wiki:TracChangeset#ChangesetHeader changesets]) indicating kind of change.\\r\\n    Clicking on this column refreshes the revision log so that it restarts\\r\\n    with this change.\\r\\n 1. The ''''''Revision'''''' number, displayed as `@xyz`.\\r\\n    This is a link to the TracBrowser, using the displayed revision as the base line.\\r\\n Next to it, you can see a little \"wheel\" icon [[Image(htdocs:../common/changeset.png)]],  which is clickable and leads to the TracChangeset view for that revision.\\r\\n 1. The ''''''Date'''''' at which the change was made.\\r\\n    The date is displayed as the time elapsed from the date of the revision. The time\\r\\n    elapsed is displayed as the number of hours, days, weeks, months, or years.\\r\\n 1. The ''''''Author'''''' of the change.\\r\\n 1. The ''''''Log Message'''''', which contains either the truncated or full commit \\r\\n    log message, depending on the value of the ''''Show full log messages'''' \\r\\n    checkbox in the form above.\\r\\n    \\r\\n\\r\\n== Inspecting Changes Between Revisions ==\\r\\n\\r\\nThe ''''View changes...'''' buttons (placed above and below the list\\r\\nof changes, on the left side) will show the set of differences\\r\\ncorresponding to the aggregated changes starting from the ''''old''''\\r\\nrevision (first radio-button) to the ''''new'''' revision (second\\r\\nradio-button), in the TracChangeset view.\\r\\n\\r\\nNote that the ''''old'''' revision doesn''t need to be actually \\r\\n''''older'''' than the ''''new'''' revision: it simply gives a base\\r\\nfor the diff. It''s therefore entirely possible to easily \\r\\ngenerate a ''''reverse diff'''', for reverting what has been done\\r\\nin the given range of revisions.\\r\\n\\r\\nFinally, if the two revisions are identical, the corresponding\\r\\nchangeset will be shown (same effect as clicking on the !ChangeSet number).\\r\\n\\r\\n== Alternative Formats ==\\r\\n\\r\\n=== The !ChangeLog Text ===\\r\\n\\r\\nAt the bottom of the page, there''s a ''''!ChangeLog'''' link\\r\\nthat will show the range of revisions as currently shown,\\r\\nbut as a simple text, matching the usual conventions for\\r\\n!ChangeLog files.\\r\\n\\r\\n=== RSS Support ===\\r\\n\\r\\nThe revision log also provides a RSS feed to monitor the changes.\\r\\nTo subscribe to a RSS feed for a file or directory, open its\\r\\nrevision log in the browser and click the orange ''XML'' icon at the bottom\\r\\nof the page. For more information on RSS support in Trac, see TracRss.\\r\\n\\r\\n----\\r\\nSee also: TracBrowser, TracChangeset, TracGuide', NULL, NULL),\n('TitleIndex', 1, 1362994208369859, 'trac', '127.0.0.1', ''''''' Index by Title '''''' | '''''' [RecentChanges Index by Date] ''''''\\r\\n\\r\\n[[TitleIndex(format=group,min=4)]]', NULL, NULL);\nINSERT INTO `wiki` (`name`, `version`, `time`, `author`, `ipnr`, `text`, `comment`, `readonly`) VALUES\n('TracFineGrainedPermissions', 1, 1362994208371393, 'trac', '127.0.0.1', '[[PageOutline(2-5, Contents, floated)]]\\r\\n= Fine grained permissions =\\r\\n\\r\\nBefore Trac 0.11, it was only possible to define fine-grained permissions checks on the repository browser sub-system.\\r\\n\\r\\nSince 0.11, there''s a general mechanism in place that allows custom **permission policy plugins** to grant or deny any action on any kind of Trac resources, even at the level of specific versions of such resources.\\r\\n\\r\\nNote that for Trac 0.12, `authz_policy` has been integrated as an optional module (in `tracopt.perm.authz_policy.*`), so it''s installed by default and can simply be activated via the //Plugins// panel in the Trac administration module.\\r\\n\\r\\n\\r\\n== Permission Policies ==\\r\\n\\r\\nA great diversity of permission policies can be implemented, and Trac comes with a few examples. \\r\\n\\r\\nWhich policies are currently active is determined by a configuration setting in TracIni:\\r\\ne.g.\\r\\n{{{\\r\\n[trac]\\r\\npermission_policies = AuthzSourcePolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy\\r\\n}}}\\r\\nThis lists the [#AuthzSourcePolicy] described below as the first policy, followed by the !DefaultPermissionPolicy which checks for the traditional coarse grained style permissions described in TracPermissions, and the !LegacyAttachmentPolicy which knows how to use the coarse grained permissions for checking the permissions available on attachments.\\r\\n\\r\\nAmong the possible optional choices, there is [#AuthzPolicy], a very generic permission policy, based on an Authz-style system. See\\r\\n[trac:source:branches/0.12-stable/tracopt/perm/authz_policy.py authz_policy.py] for details. \\r\\n\\r\\nAnother popular permission policy [#AuthzSourcePolicy], re-implements the pre-0.12 support for checking fine-grained permissions limited to Subversion repositories in terms of the new system.\\r\\n\\r\\nSee also [trac:source:branches/0.12-stable/sample-plugins/permissions sample-plugins/permissions] for more examples.\\r\\n\\r\\n\\r\\n=== !AuthzPolicy === \\r\\n==== Configuration ====\\r\\n* Install [http://www.voidspace.org.uk/python/configobj.html ConfigObj] (still needed for 0.12).\\r\\n* Copy authz_policy.py into your plugins directory (only for Trac 0.11).\\r\\n* Put a [http://swapoff.org/files/authzpolicy.conf authzpolicy.conf] file somewhere, preferably on a secured location on the server, not readable for others than the webuser. If the  file contains non-ASCII characters, the UTF-8 encoding should be used.\\r\\n* Update your `trac.ini`:\\r\\n  1. modify the [TracIni#trac-section permission_policies] entry in the `[trac]` section\\r\\n{{{\\r\\n[trac]\\r\\n...\\r\\npermission_policies = AuthzPolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy\\r\\n}}}\\r\\n  1. add a new `[authz_policy]` section\\r\\n{{{\\r\\n[authz_policy]\\r\\nauthz_file = /some/trac/env/conf/authzpolicy.conf\\r\\n}}}\\r\\n  1. enable the plugin through [/admin/general/plugin WebAdmin] or by editing the `[components]` section\\r\\n{{{\\r\\n[components]\\r\\n...\\r\\n# Trac 0.12\\r\\ntracopt.perm.authz_policy.* = enabled\\r\\n# for Trac 0.11 use this\\r\\n#authz_policy.* = enabled \\r\\n}}}\\r\\n\\r\\n\\r\\n==== Usage Notes ====\\r\\nNote that the order in which permission policies are specified is quite critical, \\r\\nas policies will be examined in the sequence provided.\\r\\n\\r\\nA policy will return either `True`, `False` or `None` for a given permission check. `True` is returned if the policy explicitly grants the permission. `False` is returned if the policy explicitly denies the permission. `None` is returned if the policy is unable to either grant or deny the permission.\\r\\n\\r\\nNOTE: Only if the return value is `None` will the ''''next'''' permission policy be consulted.\\r\\nIf none of the policies explicitly grants the permission, the final result will be `False` \\r\\n(i.e. permission denied).\\r\\n\\r\\nThe `authzpolicy.conf` file is a `.ini` style configuration file:\\r\\n{{{\\r\\n[wiki:PrivatePage@*]\\r\\njohn = WIKI_VIEW, !WIKI_MODIFY\\r\\njack = WIKI_VIEW\\r\\n* =\\r\\n}}}\\r\\n* Each section of the config is a glob pattern used to match against a Trac resource\\r\\n  descriptor. These descriptors are in the form:\\r\\n{{{\\r\\n<realm>:<id>@<version>[/<realm>:<id>@<version> ...]\\r\\n}}}\\r\\n  Resources are ordered left to right, from parent to child. If any\\r\\n  component is inapplicable, `*` is substituted. If the version pattern is\\r\\n  not specified explicitely, all versions (`@*`) is added implicitly\\r\\n\\r\\n  Example: Match the WikiStart page\\r\\n{{{\\r\\n[wiki:*]\\r\\n[wiki:WikiStart*]\\r\\n[wiki:WikiStart@*]\\r\\n[wiki:WikiStart]\\r\\n}}}\\r\\n\\r\\n  Example: Match the attachment `wiki:WikiStart@117/attachment/FOO.JPG@*`\\r\\n  on WikiStart\\r\\n{{{\\r\\n[wiki:*]\\r\\n[wiki:WikiStart*]\\r\\n[wiki:WikiStart@*]\\r\\n[wiki:WikiStart@*/attachment/*]\\r\\n[wiki:WikiStart@117/attachment/FOO.JPG]\\r\\n}}}\\r\\n\\r\\n* Sections are checked against the current Trac resource descriptor ''''''IN ORDER'''''' of\\r\\n  appearance in the configuration file. ''''''ORDER IS CRITICAL''''''.\\r\\n\\r\\n* Once a section matches, the current username is matched against the keys \\r\\n  (usernames) of the section, ''''''IN ORDER''''''. \\r\\n  * If a key (username) is prefixed with a `@`, it is treated as a group. \\r\\n  * If a value (permission) is prefixed with a `!`, the permission is\\r\\n    denied rather than granted.\\r\\n\\r\\n  The username will match any of ''anonymous'', ''authenticated'', <username> or ''*'', using normal Trac permission rules. || ''''''Note:'''''' Other groups which are created by user (e.g. by ''adding subjects to groups'' on web interface page //Admin / Permissions//) cannot be used. See [trac:ticket:5648 #5648] for details about this missing feature ||\\r\\n\\r\\nFor example, if the `authz_file` contains:\\r\\n{{{\\r\\n[wiki:WikiStart@*]\\r\\n* = WIKI_VIEW\\r\\n\\r\\n[wiki:PrivatePage@*]\\r\\njohn = WIKI_VIEW\\r\\n* = !WIKI_VIEW\\r\\n}}}\\r\\nand the default permissions are set like this:\\r\\n{{{\\r\\njohn           WIKI_VIEW\\r\\njack           WIKI_VIEW\\r\\n# anonymous has no WIKI_VIEW\\r\\n}}}\\r\\n\\r\\nThen: \\r\\n  * All versions of WikiStart will be viewable by everybody (including anonymous)\\r\\n  * !PrivatePage will be viewable only by john\\r\\n  * other pages will be viewable only by john and jack\\r\\n\\r\\nGroups:\\r\\n{{{\\r\\n[groups]\\r\\nadmins = john, jack\\r\\ndevs = alice, bob\\r\\n\\r\\n[wiki:Dev@*]\\r\\n@admins = TRAC_ADMIN\\r\\n@devs = WIKI_VIEW\\r\\n* =\\r\\n\\r\\n[*]\\r\\n@admins = TRAC_ADMIN\\r\\n* =\\r\\n}}}\\r\\n\\r\\nThen:\\r\\n- everything is blocked (whitelist approach), but\\r\\n- admins get all TRAC_ADMIN everywhere and\\r\\n- devs can view wiki pages.\\r\\n\\r\\nSome repository examples (Browse Source specific):\\r\\n{{{\\r\\n# A single repository:\\r\\n[repository:test_repo@*]\\r\\njohn = BROWSER_VIEW, FILE_VIEW\\r\\n# John has BROWSER_VIEW and FILE_VIEW for the entire test_repo\\r\\n\\r\\n# All repositories:\\r\\n[repository:*@*]\\r\\njack = BROWSER_VIEW, FILE_VIEW\\r\\n# John has BROWSER_VIEW and FILE_VIEW for all repositories\\r\\n}}}\\r\\n\\r\\nVery fine grain repository access:\\r\\n{{{\\r\\n# John has BROWSER_VIEW and FILE_VIEW access to trunk/src/some/location/ only\\r\\n[repository:test_repo@*/source:trunk/src/some/location/*@*]\\r\\njohn = BROWSER_VIEW, FILE_VIEW\\r\\n\\r\\n\\r\\n# John has BROWSER_VIEW and FILE_VIEW access to only revision 1 of all files at trunk/src/some/location only\\r\\n[repository:test_repo@*/source:trunk/src/some/location/*@1]\\r\\njohn = BROWSER_VIEW, FILE_VIEW\\r\\n\\r\\n\\r\\n# John has BROWSER_VIEW and FILE_VIEW access to all revisions of ''somefile'' at trunk/src/some/location only \\r\\n[repository:test_repo@*/source:trunk/src/some/location/somefile@*]\\r\\njohn = BROWSER_VIEW, FILE_VIEW\\r\\n\\r\\n\\r\\n# John has BROWSER_VIEW and FILE_VIEW access to only revision 1 of ''somefile'' at trunk/src/some/location only\\r\\n[repository:test_repo@*/source:trunk/src/some/location/somefile@1]\\r\\njohn = BROWSER_VIEW, FILE_VIEW\\r\\n}}}\\r\\n\\r\\nNote: In order for Timeline to work/visible for John, we must add CHANGESET_VIEW to the above permission list.\\r\\n\\r\\n\\r\\n==== Missing Features ====\\r\\nAlthough possible with the !DefaultPermissionPolicy handling (see Admin panel), fine-grained permissions still miss those grouping features (see [trac:ticket:9573 #9573], [trac:ticket:5648 #5648]). Patches are partially available, see forgotten authz_policy.2.patch  part of [trac:ticket:6680 #6680]).\\r\\n\\r\\nYou cannot do the following:\\r\\n{{{\\r\\n[groups]\\r\\nteam1 = a, b, c\\r\\nteam2 = d, e, f\\r\\nteam3 = g, h, i\\r\\ndepartmentA = team1, team2\\r\\n}}}\\r\\n\\r\\nPermission groups are not supported either. You cannot do the following:\\r\\n{{{\\r\\n[groups]\\r\\npermission_level_1 = WIKI_VIEW, TICKET_VIEW\\r\\npermission_level_2  = permission_level_1, WIKI_MODIFY, TICKET_MODIFY\\r\\n[*]\\r\\n@team1 = permission_level_1\\r\\n@team2 = permission_level_2\\r\\n@team3 = permission_level_2, TICKET_CREATE\\r\\n}}}\\r\\n\\r\\n=== !AuthzSourcePolicy  (mod_authz_svn-like permission policy) === #AuthzSourcePolicy\\r\\n\\r\\nAt the time of this writing, the old fine grained permissions system from Trac 0.11 and before used for restricting access to the repository has  been converted to a permission policy component, but from the user point of view, this makes little if no difference.\\r\\n\\r\\nThat kind of fine-grained permission control needs a definition file, which is the one used by Subversion''s mod_authz_svn. \\r\\nMore information about this file format and about its usage in Subversion is available in the  [http://svnbook.red-bean.com/en/1.5/svn.serverconfig.pathbasedauthz.html Path-Based Authorization] section in the Server Configuration chapter of the svn book.\\r\\n\\r\\nExample:\\r\\n{{{\\r\\n[/]\\r\\n* = r\\r\\n\\r\\n[/branches/calc/bug-142]\\r\\nharry = rw\\r\\nsally = r\\r\\n\\r\\n[/branches/calc/bug-142/secret]\\r\\nharry =\\r\\n}}}\\r\\n\\r\\n * ''''''/'''''' = ''''Everyone has read access by default''''\\r\\n * ''''''/branches/calc/bug-142'''''' = ''''harry has read/write access, sally read only''''\\r\\n * ''''''/branches/calc/bug-142/secret'''''' = ''''harry has no access, sally has read access (inherited as a sub folder permission)''''\\r\\n\\r\\n==== Trac Configuration ====\\r\\n\\r\\nTo activate fine grained permissions you __must__ specify the {{{authz_file}}} option in the {{{[trac]}}} section of trac.ini. If this option is set to null or not specified the permissions will not be used.\\r\\n\\r\\n{{{\\r\\n[trac]\\r\\nauthz_file = /path/to/svnaccessfile\\r\\n}}}\\r\\n\\r\\nIf you want to support the use of the `[`''''modulename''''`:/`''''some''''`/`''''path''''`]` syntax within the `authz_file`, add \\r\\n\\r\\n{{{\\r\\nauthz_module_name = modulename\\r\\n}}}\\r\\n\\r\\nwhere ''''modulename'''' refers to the same repository indicated by the `repository_dir` entry in the `[trac]` section. As an example, if the `repository_dir` entry in the `[trac]` section is {{{/srv/active/svn/blahblah}}}, that would yield the following:\\r\\n\\r\\n{{{ \\r\\n[trac]\\r\\nauthz_file = /path/to/svnaccessfile\\r\\nauthz_module_name = blahblah\\r\\n...\\r\\nrepository_dir = /srv/active/svn/blahblah \\r\\n}}}\\r\\n\\r\\nwhere the svn access file, {{{/path/to/svnaccessfile}}}, contains entries such as {{{[blahblah:/some/path]}}}.\\r\\n\\r\\n''''''Note:'''''' Usernames inside the Authz file __must__ be the same as those used inside trac. \\r\\n\\r\\nAs of version 0.12, make sure you have ''''!AuthzSourcePolicy'''' included in the permission_policies list in trac.ini, otherwise the authz permissions file will be ignored.\\r\\n\\r\\n{{{ \\r\\n[trac]\\r\\npermission_policies = AuthzSourcePolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy\\r\\n}}}\\r\\n\\r\\n==== Subversion Configuration ====\\r\\n\\r\\nThe same access file is typically applied to the corresponding Subversion repository using an Apache directive like this:\\r\\n{{{\\r\\n<Location /repos>\\r\\n  DAV svn\\r\\n  SVNParentPath /usr/local/svn\\r\\n\\r\\n  # our access control policy\\r\\n  AuthzSVNAccessFile /path/to/svnaccessfile\\r\\n</Location>\\r\\n}}}\\r\\n\\r\\nFor information about how to restrict access to entire projects in a multiple project environment see [trac:wiki:TracMultipleProjectsSVNAccess]\\r\\n\\r\\n== Debugging permissions\\r\\nIn trac.ini set:\\r\\n{{{\\r\\n[logging]\\r\\nlog_file = trac.log\\r\\nlog_level = DEBUG\\r\\nlog_type = file\\r\\n}}}\\r\\n\\r\\nAnd watch:\\r\\n{{{\\r\\ntail -n 0 -f log/trac.log | egrep ''\\\\[perm\\\\]|\\\\[authz_policy\\\\]''\\r\\n}}}\\r\\n\\r\\nto understand what checks are being performed. See the sourced documentation of the plugin for more info.\\r\\n\\r\\n\\r\\n----\\r\\nSee also: TracPermissions,\\r\\n[http://trac-hacks.org/wiki/FineGrainedPageAuthzEditorPlugin TracHacks:FineGrainedPageAuthzEditorPlugin] for a simple editor plugin.', NULL, NULL);\n"
  },
  {
    "path": "examples/webpy/index.wsgi",
    "content": "import os\n\nimport sae\nimport web\n        \nurls = (\n    '/', 'Hello'\n)\n\napp_root = os.path.dirname(__file__)\ntemplates_root = os.path.join(app_root, 'templates')\nrender = web.template.render(templates_root)\n\nclass Hello:        \n    def GET(self):\n        return render.hello()\n\napp = web.application(urls, globals()).wsgifunc()\n\napplication = sae.create_wsgi_app(app)\n"
  },
  {
    "path": "examples/webpy/templates/hello.html",
    "content": "Hello, web.py and templates\n"
  },
  {
    "path": "examples/weibo/appstack.py",
    "content": "\nfrom flask import Flask, request, redirect, session\nfrom weibopy import OAuthHandler, oauth, API\n\napp = Flask(__name__)\napp.debug = True\napp.secret_key = 'test'\n\nconsumer_key = '199***'\nconsumer_secret = 'a1f8****'\n\ndef get_referer():\n    return request.headers.get('HTTP_REFERER', '/')\n\ndef get_weibo_user():\n    auth = OAuthHandler(consumer_key, consumer_secret)\n    # Get currrent user access token from session\n    access_token = session['oauth_access_token']\n    auth.setToken(access_token.key, access_token.secret)\n    api = API(auth)\n    # Get info from weibo\n    return api.me()\n\ndef login_ok(f):\n    def login_wrapper(*args, **kw):\n        if 'oauth_access_token' not in session:\n            return redirect('/login')\n        return f(*args, **kw)\n    return login_wrapper\n\n@app.route('/')\n@login_ok\ndef hello():\n    user = get_weibo_user()\n    return \"Hello, %s <img src=%s>\" % (user.screen_name, user.profile_image_url)\n\n@app.route('/login')\ndef login():\n    session['login_ok_url'] = get_referer()\n    callback = 'http://appstack.sinaapp.com/login_callback'\n\n    auth = OAuthHandler(consumer_key, consumer_secret, callback)\n    # Get request token and login url from the provider\n    url = auth.get_authorization_url()\n    session['oauth_request_token'] = auth.request_token\n    # Redirect user to login\n    return redirect(url)\n\n@app.route('/login_callback')\ndef login_callback():\n    # This is called by the provider when user has granted permission to your app\n    verifier = request.args.get('oauth_verifier', None)\n    auth = OAuthHandler(consumer_key, consumer_secret)\n    request_token = session['oauth_request_token']\n    del session['oauth_request_token']\n    \n    # Show the provider it's us really\n    auth.set_request_token(request_token.key, request_token.secret)\n    # Ask for a temporary access token\n    session['oauth_access_token'] = auth.get_access_token(verifier)\n    return redirect(session.get('login_ok_url', '/'))\n\n@app.route('/logout')\ndef logout():\n    del session['oauth_access_token']\n    return redirect(get_referer())\n"
  },
  {
    "path": "examples/weibo/index.wsgi",
    "content": "\nimport sae\n\nfrom appstack import app\n\napplication = sae.create_wsgi_app(app)\n\n"
  }
]